অ্যান্ড্রয়েড রানটাইমে অ্যাপের আচরণ যাচাই করা হচ্ছে (ART)

অ্যান্ড্রয়েড রানটাইম (ART) হল Android 5.0 (API লেভেল 21) এবং উচ্চতর চলমান ডিভাইসগুলির জন্য ডিফল্ট রানটাইম। এই রানটাইম অনেকগুলি বৈশিষ্ট্য অফার করে যা Android প্ল্যাটফর্ম এবং অ্যাপগুলির কার্যক্ষমতা এবং মসৃণতা উন্নত করে৷ আপনি ART এর নতুন বৈশিষ্ট্য সম্পর্কে আরও তথ্য পেতে পারেন ART পরিচিতিতে

যাইহোক, ডালভিকে কাজ করে এমন কিছু কৌশল ART-তে কাজ করে না। এই দস্তাবেজটি আপনাকে ART-এর সাথে সামঞ্জস্যপূর্ণ হতে একটি বিদ্যমান অ্যাপ স্থানান্তর করার সময় দেখার বিষয়গুলি সম্পর্কে জানতে দেয়৷ ART এর সাথে চলার সময় বেশিরভাগ অ্যাপই কাজ করা উচিত।

আবর্জনা সংগ্রহ (GC) সমস্যা সমাধান করা

ডালভিকের অধীনে, অ্যাপগুলি প্রায়শই আবর্জনা সংগ্রহ (GC) প্রম্পট করতে System.gc() কে স্পষ্টভাবে কল করা দরকারী বলে মনে করে। এটি ART এর সাথে খুব কম প্রয়োজনীয় হওয়া উচিত, বিশেষ করে যদি আপনি GC_FOR_ALLOC প্রকার ঘটনা রোধ করতে বা খণ্ডিতকরণ কমাতে আবর্জনা সংগ্রহের আহ্বান জানান। আপনি System.getProperty("java.vm.version") এ কল করে কোন রানটাইম ব্যবহার করা হচ্ছে তা যাচাই করতে পারেন। ART ব্যবহার করা হলে, সম্পত্তির মান "2.0.0" বা তার বেশি।

ART সমবর্তী অনুলিপি (CC) সংগ্রাহক ব্যবহার করে যা একই সাথে জাভা হিপকে কম্প্যাক্ট করে। এই কারণে, আপনার এমন কৌশলগুলি ব্যবহার করা এড়ানো উচিত যা GC কমপ্যাক্ট করার সাথে বেমানান (যেমন অবজেক্ট ইনস্ট্যান্স ডেটাতে পয়েন্টার সংরক্ষণ করা)। জাভা নেটিভ ইন্টারফেস (JNI) ব্যবহার করে এমন অ্যাপগুলির জন্য এটি বিশেষভাবে গুরুত্বপূর্ণ। আরও তথ্যের জন্য, JNI সমস্যা প্রতিরোধ করা দেখুন।

JNI সমস্যা প্রতিরোধ

ART এর JNI ডালভিকের তুলনায় কিছুটা কঠোর। সাধারণ সমস্যাগুলি ধরতে চেকজেএনআই মোড ব্যবহার করা বিশেষভাবে ভাল ধারণা। যদি আপনার অ্যাপ C/C++ কোড ব্যবহার করে, তাহলে আপনার নিম্নলিখিত নিবন্ধটি পর্যালোচনা করা উচিত:

CheckJNI দিয়ে Android JNI ডিবাগ করা হচ্ছে

আবর্জনা সংগ্রহের সমস্যার জন্য JNI কোড পরীক্ষা করা হচ্ছে

সমবর্তী অনুলিপি (CC) সংগ্রাহক কম্প্যাকশনের জন্য মেমরিতে বস্তুগুলি সরাতে পারে। আপনি যদি C/C++ কোড ব্যবহার করেন, তাহলে কম্প্যাক্টিং GC-এর সাথে বেমানান অপারেশন করবেন না। আমরা কিছু সম্ভাব্য সমস্যা চিহ্নিত করতে চেকজেএনআই উন্নত করেছি (যেমন আইসিএস-এ JNI স্থানীয় রেফারেন্স পরিবর্তনগুলিতে বর্ণিত হয়েছে)।

বিশেষ করে দেখার জন্য একটি ক্ষেত্র হল Get...ArrayElements() এবং Release...ArrayElements() ফাংশনের ব্যবহার। নন-কম্প্যাক্টিং GC সহ রানটাইমগুলিতে, Get...ArrayElements() ফাংশনগুলি সাধারণত অ্যারে অবজেক্টের সমর্থনকারী প্রকৃত মেমরির একটি রেফারেন্স প্রদান করে। আপনি যদি প্রত্যাবর্তিত অ্যারের উপাদানগুলির একটিতে পরিবর্তন করেন, তাহলে অ্যারে অবজেক্টটি নিজেই পরিবর্তিত হয় (এবং Release...ArrayElements() এর আর্গুমেন্টগুলি সাধারণত উপেক্ষা করা হয়)। যাইহোক, যদি কমপ্যাক্ট করা GC ব্যবহার করা হয়, Get...ArrayElements() ফাংশন মেমরির একটি কপি ফেরত দিতে পারে। GC কমপ্যাক্ট করার সময় আপনি যদি রেফারেন্সের অপব্যবহার করেন, তাহলে এটি মেমরি দুর্নীতি বা অন্যান্য সমস্যার কারণ হতে পারে। উদাহরণ স্বরূপ:

  • আপনি যদি প্রত্যাবর্তিত অ্যারে উপাদানগুলিতে কোনো পরিবর্তন করেন, তাহলে আপনাকে অবশ্যই উপযুক্ত Release...ArrayElements() ফাংশনটি কল করতে হবে, যখন আপনি করা পরিবর্তনগুলি সঠিকভাবে অন্তর্নিহিত অ্যারে অবজেক্টে অনুলিপি করেছেন তা নিশ্চিত করতে।
  • আপনি যখন মেমরি অ্যারে উপাদানগুলি প্রকাশ করবেন, তখন আপনি কী পরিবর্তন করেছেন তার উপর নির্ভর করে আপনাকে অবশ্যই উপযুক্ত মোড ব্যবহার করতে হবে:
    • আপনি যদি অ্যারের উপাদানগুলিতে কোনও পরিবর্তন না করে থাকেন তবে JNI_ABORT মোড ব্যবহার করুন, যা অন্তর্নিহিত অ্যারে অবজেক্টে পরিবর্তনগুলি অনুলিপি না করে মেমরি প্রকাশ করে।
    • আপনি যদি অ্যারেতে পরিবর্তন করেন এবং রেফারেন্সের আর প্রয়োজন না হয়, তাহলে কোড 0 ব্যবহার করুন (যা অ্যারে অবজেক্ট আপডেট করে এবং মেমরির কপি মুক্ত করে)।
    • আপনি যে অ্যারেটি কমিট করতে চান তাতে যদি আপনি পরিবর্তন করেন এবং আপনি অ্যারের অনুলিপি রাখতে চান তবে JNI_COMMIT ব্যবহার করুন (যা অন্তর্নিহিত অ্যারে অবজেক্ট আপডেট করে এবং অনুলিপি ধরে রাখে)।
  • যখন আপনি Release...ArrayElements() কল করেন, তখন একই পয়েন্টারটি ফেরত দিন যা মূলত Get...ArrayElements() দ্বারা ফেরত দিয়েছিল। উদাহরণস্বরূপ, মূল পয়েন্টারটি বৃদ্ধি করা নিরাপদ নয় (প্রত্যাবর্তিত অ্যারে উপাদানগুলির মাধ্যমে স্ক্যান করতে) তারপর বৃদ্ধি করা পয়েন্টারটিকে Release...ArrayElements() এ পাস করুন। এই পরিবর্তিত পয়েন্টারটি পাস করার ফলে ভুল মেমরি মুক্ত হতে পারে, যার ফলে মেমরি নষ্ট হয়ে যায়।

ত্রুটি পরিচালনা

ART-এর JNI অনেক ক্ষেত্রে ত্রুটি ছুড়ে দেয় যেখানে ডালভিক করেন না। (আবারও, আপনি চেকজেএনআই-এর সাথে পরীক্ষা করে এমন অনেকগুলি কেস ধরতে পারেন।)

উদাহরণস্বরূপ, যদি RegisterNatives এমন একটি পদ্ধতির সাথে কল করা হয় যা বিদ্যমান নেই (সম্ভবত কারণ ProGuard এর মতো একটি টুল দ্বারা পদ্ধতিটি সরানো হয়েছিল), ART এখন সঠিকভাবে NoSuchMethodError নিক্ষেপ করে:

08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main
08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError:
    no static or non-static method
    "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I"
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.nativeLoad(Native Method)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.doLoad(Runtime.java:421)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.loadLibrary(Runtime.java:362)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.System.loadLibrary(System.java:526)

এআরটি একটি ত্রুটিও লগ করে (লগক্যাটে দৃশ্যমান) যদি RegisterNatives কোনো পদ্ধতি ছাড়াই কল করা হয়:

W/art     ( 1234): JNI RegisterNativeMethods: attempt to register 0 native
methods for <classname>

উপরন্তু, JNI ফাংশন GetFieldID() এবং GetStaticFieldID() এখন সঠিকভাবে NoSuchFieldError ছুঁড়ে দেয় শুধু শূন্য রিটার্ন করার পরিবর্তে। একইভাবে, GetMethodID() এবং GetStaticMethodID() এখন সঠিকভাবে NoSuchMethodError নিক্ষেপ করুন। এটি চেকজেএনআই ব্যর্থতার কারণ হতে পারে না পরিচালনা না করা ব্যতিক্রম বা ব্যতিক্রমগুলি নেটিভ কোডের জাভা কলারদের কাছে নিক্ষেপ করা হয়েছে। এটি চেকজেএনআই মোডের সাথে ART-সামঞ্জস্যপূর্ণ অ্যাপগুলি পরীক্ষা করা বিশেষভাবে গুরুত্বপূর্ণ করে তোলে।

ART আশা করে JNI CallNonvirtual...Method() পদ্ধতির ব্যবহারকারীরা (যেমন CallNonvirtualVoidMethod() ) পদ্ধতির ঘোষণাকারী শ্রেণী ব্যবহার করবে, একটি সাবক্লাস নয়, JNI স্পেসিফিকেশনের প্রয়োজন অনুযায়ী।

স্ট্যাক আকার সমস্যা প্রতিরোধ

ডালভিকের নেটিভ এবং জাভা কোডের জন্য আলাদা স্ট্যাক ছিল, যার একটি ডিফল্ট জাভা স্ট্যাকের আকার 32KB এবং একটি ডিফল্ট নেটিভ স্ট্যাক আকার 1MB। ভালো লোকালয়ের জন্য ART-এর একটি ইউনিফাইড স্ট্যাক রয়েছে। সাধারণত, ART Thread স্ট্যাকের আকার প্রায় ডালভিকের মতোই হওয়া উচিত। যাইহোক, যদি আপনি স্পষ্টভাবে স্ট্যাকের মাপ সেট করেন, তাহলে আপনাকে ART-তে চলমান অ্যাপগুলির জন্য সেই মানগুলিকে আবার দেখতে হবে।

  • জাভাতে, Thread কনস্ট্রাক্টরের কলগুলি পর্যালোচনা করুন যা একটি স্পষ্ট স্ট্যাকের আকার নির্দিষ্ট করে। উদাহরণস্বরূপ, StackOverflowError ঘটলে আপনাকে আকার বাড়াতে হবে।
  • C/C++-এ, JNI-এর মাধ্যমে জাভা কোড চালানোর থ্রেডগুলির জন্য pthread_attr_setstack() এবং pthread_attr_setstacksize() এর ব্যবহার পর্যালোচনা করুন। Pthread আকার খুব ছোট হলে একটি অ্যাপ JNI AttachCurrentThread() কল করার চেষ্টা করার সময় লগ করা ত্রুটির একটি উদাহরণ এখানে দেওয়া হল:
    F/art: art/runtime/thread.cc:435]
        Attempt to attach a thread with a too-small stack (16384 bytes)

অবজেক্ট মডেল পরিবর্তন

ডালভিক ভুলভাবে সাবক্লাসকে প্যাকেজ-প্রাইভেট পদ্ধতি ওভাররাইড করার অনুমতি দিয়েছে। এআরটি এই ধরনের ক্ষেত্রে একটি সতর্কতা জারি করে:

Before Android 4.1, method void com.foo.Bar.quux()
would have incorrectly overridden the package-private method in
com.quux.Quux

আপনি যদি একটি ভিন্ন প্যাকেজে একটি ক্লাসের পদ্ধতি ওভাররাইড করতে চান, তবে পদ্ধতিটিকে public বা protected হিসাবে ঘোষণা করুন।

Object এখন ব্যক্তিগত ক্ষেত্র রয়েছে। যে অ্যাপগুলি তাদের শ্রেণির শ্রেণিবিন্যাসের ক্ষেত্রে প্রতিফলিত করে সেগুলি Object ক্ষেত্রগুলি দেখার চেষ্টা না করার বিষয়ে সতর্ক হওয়া উচিত। উদাহরণ স্বরূপ, আপনি যদি সিরিয়ালাইজেশন ফ্রেমওয়ার্কের অংশ হিসাবে একটি শ্রেণির শ্রেণিবিন্যাসের পুনরাবৃত্তি করে থাকেন, তখন থামুন

Class.getSuperclass() == java.lang.Object.class

পদ্ধতিটি null আসা পর্যন্ত চালিয়ে যাওয়ার পরিবর্তে।

Proxy InvocationHandler.invoke() এখন খালি অ্যারের পরিবর্তে কোন আর্গুমেন্ট না থাকলে null পায়। এই আচরণটি আগে নথিভুক্ত করা হয়েছিল কিন্তু ডালভিকে সঠিকভাবে পরিচালনা করা হয়নি। মকিটোর পূর্ববর্তী সংস্করণগুলিতে এটির সাথে অসুবিধা রয়েছে, তাই এআরটি দিয়ে পরীক্ষা করার সময় একটি আপডেট করা মকিটো সংস্করণ ব্যবহার করুন।

AOT সংকলন সমস্যা সমাধান করা

ART এর Ahead-Of-Time (AOT) জাভা সংকলন সব স্ট্যান্ডার্ড জাভা কোডের জন্য কাজ করা উচিত। ART এর dex2oat টুল দ্বারা সংকলন করা হয়; আপনি যদি ইনস্টল করার সময় dex2oat সম্পর্কিত কোনো সমস্যার সম্মুখীন হন, তাহলে আমাদের জানান ( প্রতিবেদন সমস্যা দেখুন) যাতে আমরা যত তাড়াতাড়ি সম্ভব সেগুলি ঠিক করতে পারি। উল্লেখ্য কয়েকটি বিষয়:

  • ART ডালভিকের তুলনায় ইনস্টলের সময় আরও কঠোর বাইটকোড যাচাই করে। অ্যান্ড্রয়েড বিল্ড টুল দ্বারা উত্পাদিত কোড সূক্ষ্ম হওয়া উচিত। যাইহোক, কিছু পোস্ট-প্রসেসিং টুলস (বিশেষ করে এমন টুলস যা অস্পষ্টকরণ করে) অবৈধ ফাইল তৈরি করতে পারে যেগুলি ডালভিক সহ্য করে কিন্তু ART দ্বারা প্রত্যাখ্যাত হয়। আমরা এই ধরনের সমস্যাগুলি খুঁজে পেতে এবং ঠিক করতে টুল বিক্রেতাদের সাথে কাজ করছি। অনেক ক্ষেত্রে, আপনার সরঞ্জামগুলির সর্বশেষ সংস্করণগুলি পাওয়া এবং DEX ফাইলগুলি পুনরায় তৈরি করা এই সমস্যাগুলি সমাধান করতে পারে৷
  • এআরটি যাচাইকারী দ্বারা চিহ্নিত কিছু সাধারণ সমস্যাগুলির মধ্যে রয়েছে:
    • অবৈধ নিয়ন্ত্রণ প্রবাহ
    • ভারসাম্যহীন monitorenter / monitorexit
    • 0-দৈর্ঘ্যের প্যারামিটার প্রকার তালিকার আকার
  • কিছু অ্যাপের /system/framework , /data/dalvik-cache বা DexClassLoader এর অপ্টিমাইজ করা আউটপুট ডিরেক্টরিতে ইনস্টল করা .odex ফাইল ফরম্যাটের উপর নির্ভরশীলতা রয়েছে। এই ফাইলগুলি এখন ELF ফাইল এবং DEX ফাইলগুলির একটি বর্ধিত ফর্ম নয়৷ যদিও ART ডালভিকের মতো একই নামকরণ এবং লকিং নিয়ম অনুসরণ করার চেষ্টা করে, অ্যাপগুলি ফাইল ফর্ম্যাটের উপর নির্ভর করা উচিত নয়; বিন্যাস বিজ্ঞপ্তি ছাড়াই পরিবর্তন সাপেক্ষে.

    দ্রষ্টব্য: Android 8.0 (API লেভেল 26) এবং উচ্চতর, DexClassLoader অপ্টিমাইজড আউটপুট ডিরেক্টরি অবনত করা হয়েছে। আরও তথ্যের জন্য, DexClassLoader() কনস্ট্রাক্টরের জন্য ডকুমেন্টেশন দেখুন।

রিপোর্টিং সমস্যা

অ্যাপ JNI সমস্যার কারণে না হয় এমন কোনো সমস্যায় পড়লে, https://code.google.com/p/android/issues/list- এ Android ওপেন সোর্স প্রজেক্ট ইস্যু ট্র্যাকারের মাধ্যমে রিপোর্ট করুন। Google Play Store-এ উপলব্ধ থাকলে একটি "adb bugreport" এবং অ্যাপটির একটি লিঙ্ক অন্তর্ভুক্ত করুন৷ অন্যথায়, সম্ভব হলে, সমস্যাটি পুনরুত্পাদন করে এমন একটি APK সংযুক্ত করুন। নোট করুন যে সমস্যাগুলি (সংযুক্তিগুলি সহ) সর্বজনীনভাবে দৃশ্যমান৷