Mặc dù có nhiều tìm kiếm trên internet nhưng rất ít thông tin hữu ích được tìm thấy, cuối cùng tôi đã giải quyết được vấn đề này.
Trước hết, hãy nhớ rằng một thư viện về cơ bản cũng giống như một ứng dụng thực thi. Dĩ nhiên, các chuyên gia có thể định nghĩa chính xác hơn.
RAD Studio có thể tạo thư viện cho các hệ thống MSWindows, Linux và macOS. Chỉ cần sử dụng wizard trong IDE của bạn:
- File -> Other -> New
- Dynamic Library (MSWindows, macOS)
- Android Service (Android)
Tại đây, chúng ta có thể thấy không có tùy chọn cho Linux, nhưng Android cuối cùng không phải là Linux?
Tuy nhiên, đây không phải là điểm chính ở đây.
Điểm chính là: Sự khác biệt giữa một thư viện cho MSWindows và hệ thống Posix là gì?
Câu hỏi hay!!!
Điểm quan trọng nhất là cách mã được gọi và diễn giải bởi CPU mục tiêu.
Chắc chắn vấn đề này vượt ra ngoài kiến thức của tôi, và do đó chúng ta sẽ để nó cho các kỹ sư phụ trách.
Quay lại thế giới kiến thức khiêm tốn của tôi, như đã đề cập ở trên, RAD Studio có thể tạo ra mã sẽ được hỗ trợ bởi CPU mục tiêu mà RAD Studio hỗ trợ, vì vậy không cần đi sâu vào điểm này.
Hãy chú ý đến các điểm sau mà lập trình viên phải tuân thủ:
- Tạo tệp thư viện theo CPU mục tiêu mong muốn
- Kiểm tra mã để phát hiện các lỗi thiết kế có thể xảy ra
- Kiểm tra quyền truy cập vào đích đĩa (ít nhất là đọc và ghi trong trường hợp MSWindows, và trong Posix, quyền thực thi nếu cần)
- Triển khai thư viện đến hệ điều hành mục tiêu
- Trong phần mềm client, luôn yêu cầu người dùng cho phép truy cập đĩa, sau đó truy cập tệp thư viện để tải động
- Sau khi thực hiện các tác vụ do thư viện đề xuất, luôn kết thúc cuộc gọi, tức là giải phóng bộ nhớ do thư viện tiêu thụ
Do đó, tránh tích tụ bộ nhớ và có thể rò rỉ bộ nhớ khi kết thúc phần mềm.
Vì RAD Studio là một giao diện lập trình nâng cao, nhiều nhiệm vụ của lập trình viên được giảm xuống thành các cú nhấp chuột và lựa chọn đơn giản trên màn hình IDE.
Điều này tốt, tuy nhiên, nó làm cho nhà phát triển trở nên lười biếng và cẩu thả. Đặc biệt nếu họ là người mới bắt đầu hoặc có nhiều ứng dụng trong lĩnh vực của họ.
Chúng ta không được quên rằng người lập trình chính là bạn, không phải IDE bạn sử dụng.
Chúng ta phải coi nó như một công cụ làm việc, không phải là cái cớ để không suy nghĩ.
Bây giờ, đến với mã!
Tại đây, tôi sẽ trình diễn một cách sẽ cố gỡ bỏ sự bí ẩn khi sử dụng IDE RAD Studio, và bằng cách theo dõi, chứng minh rằng nhiều mã được tìm thấy trên internet và được sao chép rộng rãi trên nhiều blog không nên được sử dụng hoặc ít nhất nên được nghiên cứu và sử dụng theo những cách khác.
Tôi sẽ chứng minh rằng bạn có thể tạo một tệp dự án RAD Studio (*.DPR) thủ công (không sử dụng IDE RAD Studio). Tất nhiên, một số cài đặt tôi sẽ thực hiện qua IDE vì RAD Studio tạo ra nhiều tệp trợ lý trên đĩa, và tạo chúng thủ công thực sự là một việc cực nhọc mà tôi không muốn vào lúc này.
Đầu tiên, tôi sẽ tạo tệp DPR có tên "thuvienandroid.dpr", đây sẽ là thư viện (SO) của tôi cho Android 64bit.
Để làm điều đó, tôi sẽ sử dụng Notepad++ (trình chỉnh sửa tệp tổng quát) để chứng minh rằng hoàn toàn không cần sử dụng IDE của RAD Studio để tạo nó.
Vì vậy, nội dung của tệp này sẽ như sau:
program thuvienandroid;
{$R *.res}
// ---- mã này sẽ được thêm bởi IDE RAD Studio, đúng không? vì các tệp trợ lý được sử dụng bởi RAD!
// ------- sau đó, chỉ cần bỏ qua nó trong lúc này....
// uses
// donviham in 'donviham.pas';
// -----
begin
end.
Chú ý dòng "program thuvienandroid;" ... đây là sự khác biệt thực sự giữa một "Thư viện MSWindows" và một "Thư viện cho Posix".
--- MSWindows: library thuvienandroid;
--- Posix: program thuvienandroid;
Bây giờ, tôi sẽ tạo đơn vị (unit) chứa hàm của tôi trong thư viện
unit donviham;
interface
function TangGiaTri(AGiaTri: integer): integer; cdecl;
exports TangGiaTri;
implementation
function TangGiaTri(AGiaTri: integer): integer;
begin
Inc(AGiaTri);
result := AGiaTri;
end;
end.
Bây giờ, thư viện SO Android của chúng tôi đã sẵn sàng để sử dụng. Hãy sử dụng IDE để tạo tất cả các tệp trợ lý và cấu hình cần thiết để hoàn thành công việc.
Các tệp trợ lý để phát triển IDE:
- --- thuvienandroid.identcache
- --- thuvienandroid.dproj.local
- --- thuvienandroid.dproj
Trong IDE RAD Studio của bạn, chỉ cần mở tệp DPR để hoàn thành nhiệm vụ
bạn chỉ cần điều chỉnh một chút ở:
- --- Project -> Options -> Application -> Version Info: 1 hoặc bất kỳ giá trị nào khác > 0
- --- Project -> Options -> Application -> Entitlement List: chọn "Secure File Sharing"
- --- Project -> Options -> Application -> Use Permissions: chọn "read / write external storage" (nếu bạn định sử dụng... dù sao, hãy chọn nó)
- --- lưu dự án của bạn để tạo các tệp trợ lý và lưu nó vào đĩa!
Bây giờ, mọi thứ đã sẵn sàng để sử dụng!
Bây giờ, bạn có thể tạo dự án FMX cho ứng dụng client của mình như mong đợi trong việc sử dụng thông thường.
Hãy tạo một mẫu để thử nghiệm.
Trên RAD, hãy làm như sau:
- --- File -> New - Firemonkey blank project
- --- thêm yêu cầu "permissions" từ người dùng
- --- thêm mã để tải thư viện, lấy địa chỉ hàm, và giải phóng thư viện nếu không cần thiết nữa.
Dưới đây là mẫu của tôi để bạn thử nghiệm:
unit uFormChinh;
interface
uses
System.SysUtils,
System.Types,
System.UITypes,
System.Classes,
System.Variants,
FMX.Types,
FMX.Controls,
FMX.Forms,
FMX.Graphics,
FMX.Dialogs,
FMX.Controls.Presentation,
FMX.StdCtrls,
System.Permissions,
FMX.Memo.Types,
FMX.ScrollBox,
FMX.Memo;
type
TFormChinh = class(TForm)
BtnYeuQuyen: TButton;
BtnTaiThuVien: TButton;
MemoKetQua: TMemo;
procedure BtnYeuQuyenClick(Sender: TObject);
procedure BtnTaiThuVienClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
procedure XuLyQuyenHan(Sender: TObject; const AQuyen: TClassicStringDynArray; const AKetQua: TClassicPermissionStatusDynArray);
public
{ Public declarations }
end;
var
FormChinh: TFormChinh;
implementation
uses
System.IOUtils,
Androidapi.JNI.JavaTypes,
Androidapi.Helpers,
Androidapi.JNI.Os;
{$R *.fmx}
const
TenThuVienSO: string = 'libthuvienandroid.so';
TenHam: string = 'TangGiaTri';
var
DuongDanThuVien: string;
HamThuVien : function(AGiaTri: integer): integer; cdecl;
procedure TFormChinh.XuLyQuyenHan(Sender: TObject; const AQuyen: TClassicStringDynArray; const AKetQua: TClassicPermissionStatusDynArray);
begin
if PermissionsService.IsEveryPermissionGranted([ //
JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE), { }
JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE)]) then
begin
BtnTaiThuVien.Enabled := true;
MemoKetQua.Lines.Add('permission được chấp nhận bởi người dùng');
end else begin
BtnTaiThuVien.Enabled := false;
MemoKetQua.Lines.Add('permission bị từ chối bởi người dùng');
end;
end;
procedure TFormChinh.BtnYeuQuyenClick(Sender: TObject);
begin
PermissionsService.RequestPermissions([ { }
JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE), { }
JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE)], { }
XuLyQuyenHan);
end;
procedure TFormChinh.FormCreate(Sender: TObject);
begin
DuongDanThuVien := IncludeTrailingPathDelimiter(TPath.GetDocumentsPath) + TenThuVienSO;
end;
procedure TFormChinh.BtnTaiThuVienClick(Sender: TObject);
var
XThuVien: THandle;
KQ: integer;
begin
XThuVien := 0;
@HamThuVien := nil;
//
//
try
MemoKetQua.Lines.Add('đang cố gắng SafeLoadLibrary(DuongDanThuVien)... bắt đầu...');
//
MemoKetQua.Lines.Add('Đường dẫn thư viện: ' + DuongDanThuVien);
MemoKetQua.Lines.Add('Đường dẫn thư viện: ' + PWideChar(DuongDanThuVien));
//
(*
SafeLoadLibrary đơn giản như một sự thay thế cho LoadLibrary.
Nó có tham số thứ hai được sử dụng để gọi SetErrorMode.
Tham số đó là tùy chọn và mặc định là SEM_NOOPENFILEERRORBOX không phải lúc nào cũng là những gì bạn muốn.
nếu DLL bạn đang tải phụ thuộc vào các DLL khác không thể được tải, bạn sẽ nhận được hộp thoại lỗi hệ thống.
nếu không có hộp thoại lỗi, không có thông tin về "lỗi" ... phải không?
trong biên dịch Posix:
-- LoadLibrary(...), thực tế, sẽ gọi hàm: dlopen()
-- GetProcAddress(...), thực tế, sẽ gọi hàm: dlsym(), dladdr(), dlopne() và dlclose()
-- FreeLibrary(...), thực tế, sẽ gọi hàm: dlclose()
*)
// XThuVien := SafeLoadLibrary(DuongDanThuVien);
XThuVien := LoadLibrary(PWideChar(DuongDanThuVien)); // sử dụng hàm Posix: dlopen(....)
//
if (XThuVien > 0) then
begin
MemoKetQua.Lines.Add('XThuVien = ' + XThuVien.ToString);
//
@HamThuVien := GetProcAddress(XThuVien, PWideChar(TenHam)); // sử dụng hàm Posix: dlsym(....), dladdr(...)...
//
if Assigned(HamThuVien) then
begin
KQ := HamThuVien(255);
MemoKetQua.Lines.Add('255 + 1 = ' + KQ.ToString);
end
else
MemoKetQua.Lines.Add('HamThuVien = nil');
end
else
MemoKetQua.Lines.Add('XThuVien = 0');
finally
FreeLibrary(XThuVien); // sử dụng hàm Posix: dlclose(....)
//
MemoKetQua.Lines.Add('FreeLibrary(XThuVien)... hoàn thành');
end;
end;
end.
Và đó là tất cả mọi người!
Chúc vui vẻ với điều này!
Đừng quên triển khai tệp .so như hình dưới đây: