অ্যান্ড্রয়েড এপিআই-এর জন্য লাইব্রেরি র্যাপার অ্যান্ড্রয়েড গেম ডেভেলপমেন্ট কিটের অংশ।
লাইব্রেরি র্যাপার হল একটি কমান্ড-লাইন টুল (CLI) যা জাভাতে লেখা Android API-এর জন্য C-ভাষা র্যাপার কোড তৈরি করে। ম্যানুয়ালি জাভা নেটিভ ইন্টারফেস বা JNI তৈরি না করেই আপনি জাভা API কল করতে নেটিভ অ্যান্ড্রয়েড অ্যাপে এই কোডটি ব্যবহার করতে পারেন। এই টুলটি প্রাথমিকভাবে C বা C++ এ লেখা অ্যান্ড্রয়েড অ্যাপের বিকাশকে সহজ করতে পারে।
টুলটি আপনার প্রদান করা জাভা আর্কাইভ (JAR) ফাইলে থাকা পাবলিক সিম্বল বা টুলের কনফিগারেশন ফাইলে সংজ্ঞায়িত ক্লাস বা উভয়ের জন্য C কোড তৈরি করে কাজ করে। টুল দ্বারা উত্পন্ন কোড জাভা API প্রতিস্থাপন করে না; পরিবর্তে, এটি আপনার সি কোড এবং জাভার মধ্যে একটি সেতু হিসাবে কাজ করে। আপনার প্রোজেক্টে অন্তর্ভুক্ত করার জন্য আপনার অ্যাপের এখনও আপনার মোড়ানো জাভা লাইব্রেরি প্রয়োজন।
ডাউনলোড করুন
লাইব্রেরি র্যাপার সংরক্ষণাগারটি ডাউনলোড করুন এবং আপনার পছন্দের ডিরেক্টরিতে এর সামগ্রীটি আনপ্যাক করুন।
সিনট্যাক্স
লাইব্রেরি র্যাপার টুলটিতে নিম্নলিখিত কমান্ড লাইন সিনট্যাক্স রয়েছে:
java -jar lw.jar \
[-i jar-file-to-be-wrapped] \
[-o output-path] \
[-c config-file] \
[-fa allow-list-file] \
[-fb block-list-file] \
[--skip_deprecated_symbols]
প্যারামিটার | বর্ণনা |
---|---|
-i jar-file-to-be-wrapped | C র্যাপার কোড তৈরি করতে JAR ফাইল। একাধিক JAR নির্দিষ্ট করা যেতে পারে, উদাহরণস্বরূপ:-i first_library.jar -i second_library.jar... |
-o output-path | উত্পন্ন কোডের জন্য ফাইল সিস্টেম অবস্থান। |
-c config-file | লাইব্রেরি র্যাপার কনফিগারেশন ফাইলে ফাইল সিস্টেম পাথ। বিস্তারিত জানার জন্য, কনফিগারেশন বিভাগটি দেখুন। |
-fa allow-list-file | একটি ফিল্টার ফাইলের একটি পথ যেখানে আপনি টুলটি মোড়ানোর জন্য প্রতীক নির্দিষ্ট করতে পারেন। বিস্তারিত জানার জন্য, ফিল্টার বিভাগটি দেখুন। |
-fb block-list-file | র্যাপিং থেকে বাদ দেওয়া চিহ্ন সমন্বিত একটি ফিল্টার ফাইলের পথ। বিস্তারিত জানার জন্য, ফিল্টার বিভাগটি দেখুন। |
--skip_deprecated_symbols | র্যাপার টুলকে @Deprecated চিহ্নগুলি এড়িয়ে যাওয়ার নির্দেশ দেয়। |
মোড়ানো কনফিগারেশন ফাইল
লাইব্রেরি র্যাপার কনফিগারেশন হল একটি JSON ফাইল যা আপনাকে কোড জেনারেশন প্রক্রিয়া নিয়ন্ত্রণ করতে দেয়। ফাইলটি নিম্নলিখিত কাঠামো ব্যবহার করে।
{
// An array of type-specific configs. A type config is useful when a user wants to map
// a Java type to a manually defined C type without generating the code. For example, when a developer
// has their own implementation of the "java.lang.String" class, they can tell the generator to use it
// instead of generating it.
"type_configs": [
{
// [Required] Name of a fully qualified Java type.
"java_type": "java.lang.String",
// The C type that the java_type will be mapped to.
"map_to": "MyOwnStringImplementation",
// A header file that contains the declaration of the "map_to" type.
"source_of_definition": "my_wrappers/my_own_string_implementation.h",
// Controls if a value should be passed by pointer or value.
"pass_by_value": false
}
],
// An array of package-specific configs.
"package_configs": [
{
// [Required] A name of a Java package that this section regards. A wildchar * can be used at the
// end of the package name to apply this config to all packages whose name starts with this value.
"package_name": "androidx.core.app*",
// A subdirectory relative to the root directory where the generated code will be located.
"sub_directory": "androidx_generated/",
// If true, the generated file structure reflects the package name. For example, files generated
// for the package com.google.tools will be placed in the directory com/google/tools/.
"file_location_by_package_name": true,
// A prefix added to all class names from this package.
"code_prefix": "Gen",
// A prefix added to all generated file names from this package.
"file_prefix": = "gen_"
}
],
// An array of manually defined classes for wrapping. Defining classes manually is useful when a
// jar file with desired classes are not available or a user needs to wrap just a small part of an SDK.
"custom_classes": [
{
// [Required] A fully-qualified Java class name. To define inner class, use symbol "$", for example
// "class com.example.OuterClass$InnerClass".
"class_name": "class java.util.ArrayList<T>",
// List of methods.
"methods": [
"ArrayList()", // Example of a constructor.
"boolean add(T e)", // Example of a method that takes a generic parameter.
"T get(int index)", // Example of a method that returns a generic parameter.
"int size()" // Example of parameterless method.
]
},
]
}
ফিল্টার ফাইল
আপনি যে JAR ফাইলগুলি মোড়ানোর পরিকল্পনা করছেন তা থেকে কিছু প্রতীক বাদ দেওয়া কার্যকর হতে পারে। প্রতীকগুলি বাদ দিতে আপনি আপনার কনফিগারেশনে একটি ফিল্টার ফাইল নির্দিষ্ট করতে পারেন। একটি ফিল্টার ফাইল একটি সাধারণ পাঠ্য ফাইল যেখানে প্রতিটি লাইন মোড়ানোর জন্য একটি চিহ্ন সংজ্ঞায়িত করে। ফিল্টার ফাইল নিম্নলিখিত সিনট্যাক্স ব্যবহার করে:
java-symbol-name java-jni-type-signature
নিম্নলিখিত একটি উদাহরণ ফিল্টার ফাইল:
# Class filter
java.util.ArrayList Ljava.util.ArrayList;
# Method filter
java.util.ArrayList.lastIndexOf (Ljava.lang.Object;)I
# Field filter
android.view.KeyEvent.KEYCODE_ENTER I
আপনি কনফিগারেশনে -fa
প্যারামিটার ব্যবহার করে অনুমোদিত চিহ্ন এবং -fb
প্যারামিটার ব্যবহার করে ব্লক করা প্রতীকগুলি নির্দিষ্ট করে একটি ফিল্টার ফাইল প্রদান করেন। উভয় পরামিতি একযোগে ব্যবহার করা যেতে পারে। যদি উভয় ফিল্টার প্রদান করা হয়, একটি প্রতীক আবৃত করা হবে যখন এটি অনুমোদিত ফিল্টার ফাইলে সংজ্ঞায়িত করা হয় এবং ব্লক ফিল্টার ফাইলে উপস্থিত না থাকে।
উদাহরণ দৃশ্যকল্প
অনুমান করুন যে আপনাকে JAR ফাইলটি ChatLibrary.jar
র্যাপ করতে হবে যার মধ্যে নিম্নলিখিত ক্লাস রয়েছে:
public class ChatManager {
public static void sendMessage(int userId, String message) {...}
}
আপনার C প্রজেক্টের জন্য আপনাকে এই JAR-এর জন্য একটি নেটিভ র্যাপার তৈরি করতে হবে, যা আপনার নেটিভ অ্যান্ড্রয়েড অ্যাপকে রানটাইমের সময় কল করতে সক্ষম করে। নিম্নলিখিত কমান্ডের সাহায্যে লাইব্রেরি মোড়ক ব্যবহার করে এই কোডটি তৈরি করুন:
java -jar lw.jar -i ChatLibrary.jar -o ./generated_code/
পূর্ববর্তী কমান্ড ./generated_code
ডিরেক্টরিতে C সোর্স কোড তৈরি করে। জেনারেট করা ফাইল chat_manager.h
নিম্নলিখিতগুলির মতো কোড রয়েছে, যা আপনাকে আপনার প্রকল্পের লাইব্রেরিতে কল করতে সক্ষম করে:
#include "java/lang/string.h"
typedef struct ChatManager_ ChatManager;
void ChatManager_sendMessage(int32_t user_id, String* message);
একটি গভীর উদাহরণের জন্য, লাইব্রেরি মোড়ক নির্দেশিকা দেখুন।
টুল বিস্তারিত
নিম্নলিখিত বিভাগগুলি লাইব্রেরি মোড়কের কার্যকারিতার বিস্তারিত তথ্য প্রদান করে।
আউটপুট ডিরেক্টরি গঠন
সমস্ত সি সোর্স এবং হেডার ফাইল সাবডিরেক্টরিতে অবস্থিত যা মোড়ানো জাভা ক্লাসের প্যাকেজ নামকে প্রতিফলিত করে। উদাহরণস্বরূপ, নির্দিষ্ট JAR java.lang.Integer
এর জন্য র্যাপার কোড ./java/lang/integer.[h/cc]
ডিরেক্টরিতে তৈরি করা হয়।
আপনি টুলের কনফিগারেশন ফাইল ব্যবহার করে এই আউটপুট আচরণ নিয়ন্ত্রণ করতে পারেন।
বস্তুর জীবনচক্র
জাভা অবজেক্টগুলিকে সি কোডে অস্বচ্ছ পয়েন্টার হিসাবে উপস্থাপন করা হয়, যাকে বলা হয় মোড়ক। একটি মোড়ক একটি সংশ্লিষ্ট জাভা অবজেক্টের জন্য একটি JNI রেফারেন্স পরিচালনা করে। নিম্নলিখিত পরিস্থিতিতে একটি মোড়ক তৈরি করা যেতে পারে:
-
MyClass _wrapJniReference(jobject jobj)
ফাংশন কল করে একটি বিদ্যমান JNI রেফারেন্স মোড়ানোর মাধ্যমে। ফাংশনটি প্রদত্ত রেফারেন্সের মালিকানা নেয় না, তবে নিজস্ব গ্লোবাল JNI রেফারেন্স তৈরি করে। - একটি নতুন অবজেক্ট তৈরি করে, যা জাভাতে একজন কনস্ট্রাক্টরকে কল করার সমতুল্য:
MyClass _construct()
- একটি ফাংশন থেকে একটি নতুন মোড়ক ফেরত দিয়ে, উদাহরণস্বরূপ:
Score* Leaderboard_getScore(Leaderboard* instance, String* leaderboard_name)
যখন সেগুলি আর ব্যবহার করা হবে না তখন আপনাকে সমস্ত মোড়ক ধ্বংস করতে হবে৷ এটি করতে, ডেডিকেটেড destroy()
ফাংশনটিকে কল করুন MyClass _destroy( MyClass * instance)
।
যে ফাংশনগুলি র্যাপারগুলি ফেরত দেয় সেগুলি প্রতিটি কলের জন্য তাদের জন্য একটি নতুন মেমরি বরাদ্দ করে, এমনকি যদি র্যাপারগুলি একই জাভা উদাহরণ উপস্থাপন করে।
উদাহরণস্বরূপ, যখন জাভা পদ্ধতি Singleton.getInstance()
সর্বদা একই উদাহরণ প্রদান করে, তখন C পাশের সমতুল্য ফাংশন একই জাভা দৃষ্টান্তের জন্য একটি র্যাপারের একটি নতুন উদাহরণ তৈরি করবে:
Singleton* singleton_a = Singleton_getInsance();
Singleton* singleton_b = Singleton_getInsance();
// singleton_a and singleton_b are different pointers, even though they represent the same Java instance.
রেফারেন্সহীন ক্লাস পরিচালনা করুন
যখন একটি প্রদত্ত JAR-এ একটি ক্লাস পাওয়া যায় না, তখন লাইবারারি র্যাপার একটি অস্বচ্ছ পয়েন্টার এবং নিম্নলিখিত পদ্ধতিগুলির সমন্বয়ে একটি মৌলিক বাস্তবায়ন তৈরি করে:
-
wrapJniReference()
-
getJniReference()
-
destroy()
কোড প্রজন্মের বিবরণ
যখন চালানো হয়, লাইব্রেরি র্যাপার আপনার টুল প্রদান করা JAR ফাইল(গুলি) এর পাবলিক চিহ্নের উপর ভিত্তি করে C কোড তৈরি করে। জেনারেট করা সি কোড মোড়ানো জাভা কোড থেকে পার্থক্য প্রদর্শন করতে পারে। উদাহরণস্বরূপ, সি ওওপি, জেনেরিক প্রকার, পদ্ধতি ওভারলোডিং বা অন্যান্য জাভা বৈশিষ্ট্যগুলির মতো বৈশিষ্ট্যগুলিকে সমর্থন করে না।
এই পরিস্থিতিগুলিকে প্রতিফলিত করে জেনারেট করা C কোড C ডেভেলপারদের দ্বারা প্রত্যাশিত কোডের প্রকার থেকে আলাদা হতে পারে। নিম্নলিখিত বিভাগগুলির উদাহরণগুলি কীভাবে জাভা কোড থেকে টুলটি C তৈরি করতে পারে তার প্রসঙ্গ প্রদান করে। দ্রষ্টব্য: কোড স্নিপেটগুলিতে, নিম্নলিখিত উদাহরণগুলির মধ্যে C/C++ এবং জাভা কোড স্নিপেট অন্তর্ভুক্ত রয়েছে। এই স্নিপেটগুলি শুধুমাত্র প্রদর্শনের উদ্দেশ্যে করা হয়েছে কিভাবে টুলটি প্রতিটি প্রদত্ত পরিস্থিতির জন্য কোড তৈরি করে।
ক্লাস
ক্লাসগুলি সি-তে অস্বচ্ছ পয়েন্টার হিসাবে উপস্থাপিত হয়:
C/C++
typedef struct MyClass_ MyClass;
জাভা
public class MyClass { ... }
অস্বচ্ছ পয়েন্টারগুলির উদাহরণগুলিকে মোড়ক হিসাবে উল্লেখ করা হয়। র্যাপার টুল প্রতিটি ক্লাসের জন্য অতিরিক্ত সমর্থন ফাংশন তৈরি করে। পূর্ববর্তী উদাহরণ ক্লাস MyClass
এর জন্য, নিম্নলিখিত ফাংশন তৈরি করা হয়:
// Wraps a JNI reference with MyClass. The 'jobj' must represent MyClass on the Java side.
MyClass* MyClass_wrapJniReference(jobject jobj);
// Return JNI reference associated with the 'MyClass' pointer.
jobject MyClass_getJniReference(const MyClass* object);
// Destroys the object and releases underlying JNI reference.
void MyClass_destroy(const MyClass* object);
কনস্ট্রাক্টর
পাবলিক বা ডিফল্ট কনস্ট্রাক্টর সহ ক্লাসগুলি বিশেষ ফাংশন ব্যবহার করে উপস্থাপন করা হয়:
C/C++
MyClass* MyClass_construct(String* data);
জাভা
public class MyClass {
public MyClass(String data) { ... }
}
পদ্ধতি
পদ্ধতি স্বাভাবিক ফাংশন হিসাবে উপস্থাপিত হয়. একটি ফাংশনের নামের মধ্যে মূল ক্লাসের নাম থাকে। নন-স্ট্যাটিক ইনস্ট্যান্স পদ্ধতির প্রতিনিধিত্বকারী ফাংশনগুলির প্রথম প্যারামিটার হিসাবে একটি জাভা অবজেক্টের প্রতিনিধিত্বকারী কাঠামোর একটি নির্দেশক থাকে, যার পক্ষে ফাংশন বলা হয়। এই পদ্ধতিটি this
পয়েন্টারের সাথে সাদৃশ্যপূর্ণ।
C/C++
Result* MyClass_doAction(const MyClass* my_class_instance, int32_t action_id, String* data);
int32_t MyClass_doAction(int32_t a, int32_t b);
জাভা
public class MyClass {
public Result doAction(int actionId, String data) { ... }
public static int doCalculations(int a, int b) { ... }
}
অভ্যন্তরীণ ক্লাস
অভ্যন্তরীণ শ্রেণীগুলিকে সাধারণ শ্রেণীর সাথে ঘনিষ্ঠভাবে উপস্থাপন করা হয়, সংশ্লিষ্ট C কাঠামোর নাম ব্যতীত বাইরের শ্রেণীর শৃঙ্খলিত নামগুলি রয়েছে:
C/C++
typedef struct MyClass_InnerClass_ MyClass_InnerClass;
জাভা
public class MyClass {
public class InnerClass {...}
}
অভ্যন্তরীণ ক্লাস পদ্ধতি
অভ্যন্তরীণ শ্রেণীর পদ্ধতিগুলি নিম্নরূপ উপস্থাপন করা হয়:
C/C++
bool MyClass_InnerClass_setValue(MyClass_InnerClass* my_class_inner_class_instance, int32_t value);
জাভা
public class MyClass {
public class InnerClass {
public boolean setValue(int value) { ... }
}
}
জেনেরিক প্রকার
লাইব্রেরি র্যাপার জেনেরিক প্রকারগুলিকে সরাসরি মোড়ানো হয় না৷ পরিবর্তে, টুলটি শুধুমাত্র জেনেরিক টাইপ ইনস্ট্যান্টেশনের জন্য মোড়ক তৈরি করে।
উদাহরণস্বরূপ, যখন একটি API-তে MyGeneric<T>
ক্লাস বিদ্যমান থাকে, এবং এই শ্রেণীর দুটি ইন্সট্যান্টেশন থাকে, যেমন MyGeneric<Integer>
এবং MyGeneric<String>
, তখন সেই দুটি ইনস্ট্যান্টেশনের জন্য র্যাপার তৈরি হয়। এর মানে হল আপনি বিভিন্ন ধরনের কনফিগারেশন ব্যবহার করে MyGeneric<T>
টাইপের নতুন ইনস্ট্যান্টেশন তৈরি করতে পারবেন না। নিম্নলিখিত উদাহরণ দেখুন:
C/C++
// result.h
typedef struct Result_Integer_ Result_Integer;
typedef struct Result_Float_ Result_Float;
Integer* Result_Integer_getResult(const Result_Integer* instance);
Float* Result_Float_getResult(const Result_Float* instance);
// data_processor.h
typedef struct DataProcessor_ DataProcessor;
Result_Integer* DataProcessor_processIntegerData(const DataProcessor* instance);
Result_Float* DataProcessor_processFloatData(constDataProcessor* instance);
জাভা
public class Result<T> {
public T getResult();
}
public class DataProcessor {
public Result<Integer> processIntegerData();
public Result<Float> processFloatData();
}
ইন্টারফেস বাস্তবায়ন
implementInterface()
কল করে একটি সি ইন্টারফেস প্রয়োগ করুন এবং প্রতিটি ইন্টারফেস পদ্ধতির জন্য একটি কলব্যাক ফাংশন প্রদান করুন। শুধুমাত্র ইন্টারফেস এই পদ্ধতিতে প্রয়োগ করা যেতে পারে; ক্লাস এবং বিমূর্ত ক্লাস সমর্থিত নয়। নিম্নলিখিত উদাহরণ দেখুন:
C/C++
// observer.h
typedef struct Observer_ Observer;
typedef void (*Observer_onAction1Callback)();
typedef void (*Observer_onAction2Callback)(int32_t data);
Observer* Observer_implementInterface(
Observer_onAction1Callback observer_on_action1_callback,
Observer_onAction2Callback observer_on_action2_callback);
জাভা
public interface Observer {
void onAction1();
void onAction2(int data);
}
public class Subject {
public void registerObserver(Observer observer);
}
ব্যবহারের উদাহরণ:
void onAction1() {
// Handle action 1
}
void onAction2(int32_t data) {
// Handle action 2
}
Observer* observer = Observer_implementInterface(onAction1, onAction2);
Subject_registerObserver(subject, observer);
সীমাবদ্ধতা
লাইব্রেরি র্যাপার টুল বিটাতে আছে। আপনি নিম্নলিখিত সীমাবদ্ধতার সম্মুখীন হতে পারেন:
অসমর্থিত জাভা নির্মাণ
লাইব্রেরি মোড়ক বিটা নিম্নলিখিত গঠন সমর্থন করে না:
পদ্ধতি ওভারলোডিং
সি ভাষা একই নামের দুটি ফাংশন ঘোষণা করার অনুমতি দেয় না। যদি ক্লাস পদ্ধতি ওভারলোডিং ব্যবহার করে, তাহলে জেনারেট করা সি কোড কম্পাইল হবে না। সমাধান হল পর্যাপ্ত পরিমাপের সেট সহ শুধুমাত্র একটি পদ্ধতি ব্যবহার করা। অবশিষ্ট ফাংশন ফিল্টার ব্যবহার করে ফিল্টার আউট করা যেতে পারে. এটি নির্মাণকারীদের ক্ষেত্রেও প্রযোজ্য।
টেমপ্লেট করা পদ্ধতি
static final int
এবংstatic final String
ছাড়া অন্য ক্ষেত্রঅ্যারে
সম্ভাব্য নামের সংঘর্ষ
সি কোডে জাভা ক্লাসগুলি কীভাবে উপস্থাপন করা হয় তার কারণে, খুব বিরল ক্ষেত্রে নামের দ্বন্দ্ব হতে পারে। উদাহরণ স্বরূপ, Foo
ক্লাসের ভিতরে একটি ক্লাস Foo<Bar>
এবং একটি অভ্যন্তরীণ ক্লাস Bar
সি-তে একই চিহ্ন দ্বারা উপস্থাপিত হয়: typedef struct Foo_Bar_ Foo_Bar;
সমর্থন
আপনি যদি লাইব্রেরির মোড়কের সাথে কোনও সমস্যা খুঁজে পান তবে দয়া করে আমাদের জানান।
বাগ ব্রাউজ করুন | একটি বাগ ফাইল করুন |
---|---|
ইঞ্জিনিয়ারিং | bug_report |
ডকুমেন্টেশন | bug_report |