উন্নত বিষয়

এই বিভাগগুলি রেফারেন্সের জন্য বোঝানো হয়েছে এবং এটি আপনাকে উপরের থেকে নীচে পড়ার প্রয়োজন নেই।

ফ্রেমওয়ার্ক API ব্যবহার করুন:

এই APIগুলি আরও সামঞ্জস্যপূর্ণ API পৃষ্ঠের জন্য SDK-তে মোড়ানো হবে (যেমন UserHandle অবজেক্টগুলি এড়িয়ে যাওয়া), কিন্তু আপাতত, আপনি এগুলিকে সরাসরি কল করতে পারেন৷

বাস্তবায়ন সহজবোধ্য: আপনি যদি যোগাযোগ করতে পারেন, এগিয়ে যান। যদি না হয়, তবে আপনি অনুরোধ করতে পারেন, তারপর আপনার ব্যবহারকারীর প্রম্পট/ব্যানার/টুলটিপ/ইত্যাদি দেখান। ব্যবহারকারী সেটিংসে যেতে রাজি হলে, অনুরোধের উদ্দেশ্য তৈরি করুন এবং ব্যবহারকারীকে সেখানে পাঠাতে Context#startActivity ব্যবহার করুন। এই ক্ষমতা কখন পরিবর্তিত হয় তা সনাক্ত করতে আপনি হয় সম্প্রচার ব্যবহার করতে পারেন, অথবা ব্যবহারকারী ফিরে আসলে আবার পরীক্ষা করতে পারেন।

এটি পরীক্ষা করার জন্য, আপনাকে আপনার কাজের প্রোফাইলে TestDPC খুলতে হবে, একেবারে নীচে যান এবং সংযুক্ত অ্যাপের অনুমতি তালিকায় আপনার প্যাকেজের নাম যোগ করতে নির্বাচন করুন৷ এটি অ্যাডমিনকে আপনার অ্যাপের 'অনুমতি-তালিকা দেওয়ার' অনুকরণ করে।

শব্দকোষ

এই বিভাগটি ক্রস-প্রোফাইল বিকাশের সাথে সম্পর্কিত মূল পদগুলিকে সংজ্ঞায়িত করে।

ক্রস প্রোফাইল কনফিগারেশন

একটি ক্রস প্রোফাইল কনফিগারেশন গ্রুপগুলি একসাথে সম্পর্কিত ক্রস প্রোফাইল প্রদানকারী ক্লাস এবং ক্রস-প্রোফাইল বৈশিষ্ট্যগুলির জন্য সাধারণ কনফিগারেশন প্রদান করে। সাধারণত কোডবেস প্রতি একটি @CrossProfileConfiguration টীকা থাকবে, কিন্তু কিছু জটিল অ্যাপ্লিকেশনে একাধিক হতে পারে।

প্রোফাইল সংযোগকারী

একটি সংযোগকারী প্রোফাইলের মধ্যে সংযোগ পরিচালনা করে। সাধারণত প্রতিটি ক্রস প্রোফাইল টাইপ একটি নির্দিষ্ট সংযোগকারীকে নির্দেশ করবে। একটি একক কনফিগারেশনের প্রতিটি ক্রস প্রোফাইল টাইপ একই সংযোগকারী ব্যবহার করতে হবে।

ক্রস প্রোফাইল প্রদানকারী ক্লাস

একটি ক্রস প্রোফাইল প্রোভাইডার ক্লাস গ্রুপগুলি একসাথে সম্পর্কিত ক্রস প্রোফাইল প্রকারগুলি।

মধ্যস্থতাকারী

একজন মধ্যস্থতাকারী উচ্চ-স্তরের এবং নিম্ন-স্তরের কোডের মধ্যে বসে, সঠিক প্রোফাইলে কল বিতরণ করে এবং ফলাফল একত্রিত করে। এটি একমাত্র কোড যা প্রোফাইল-সচেতন হওয়া দরকার। এটি SDK-তে নির্মিত কিছুর পরিবর্তে একটি স্থাপত্য ধারণা।

ক্রস প্রোফাইল টাইপ

একটি ক্রস প্রোফাইল টাইপ হল একটি ক্লাস বা ইন্টারফেস যেখানে @CrossProfile টীকা করা পদ্ধতি রয়েছে। এই ধরনের কোড প্রোফাইল-সচেতন হতে হবে না এবং আদর্শভাবে শুধুমাত্র তার স্থানীয় ডেটাতে কাজ করা উচিত।

প্রোফাইলের ধরন

প্রোফাইলের ধরন
কারেন্ট সক্রিয় প্রোফাইল যা আমরা সম্পাদন করছি।
অন্যান্য (যদি এটি বিদ্যমান থাকে) আমরা যে প্রোফাইলটি চালাচ্ছি না।
ব্যক্তিগত ব্যবহারকারী 0, ডিভাইসে প্রোফাইল যা বন্ধ করা যাবে না।
কাজ সাধারণত ব্যবহারকারী 10 কিন্তু বেশি হতে পারে, টগল চালু এবং বন্ধ করা যেতে পারে, কাজের অ্যাপ এবং ডেটা ধারণ করতে ব্যবহৃত হয়।
প্রাথমিক ঐচ্ছিকভাবে অ্যাপ্লিকেশন দ্বারা সংজ্ঞায়িত. প্রোফাইল যা উভয় প্রোফাইলের একটি মার্জড ভিউ দেখায়।
মাধ্যমিক যদি প্রাথমিক সংজ্ঞায়িত করা হয়, সেকেন্ডারি হল প্রোফাইল যা প্রাথমিক নয়।
সরবরাহকারী প্রাথমিক প্রোফাইলের সরবরাহকারী উভয়ই প্রোফাইল, সেকেন্ডারি প্রোফাইলের সরবরাহকারী শুধুমাত্র সেকেন্ডারি প্রোফাইল নিজেই।

প্রোফাইল শনাক্তকারী

একটি ক্লাস যা এক ধরনের প্রোফাইল (ব্যক্তিগত বা কাজ) প্রতিনিধিত্ব করে। এগুলি এমন পদ্ধতি দ্বারা ফেরত দেওয়া হবে যা একাধিক প্রোফাইলে চলে এবং সেই প্রোফাইলগুলিতে আরও কোড চালানোর জন্য ব্যবহার করা যেতে পারে। এই সুবিধাজনক স্টোরেজ জন্য একটি int ক্রমিক করা যেতে পারে.

এই নির্দেশিকাটি আপনার Android অ্যাপের মধ্যে দক্ষ এবং রক্ষণাবেক্ষণযোগ্য ক্রস-প্রোফাইল কার্যকারিতা তৈরি করার জন্য প্রস্তাবিত কাঠামোর রূপরেখা দেয়।

আপনার CrossProfileConnector একটি সিঙ্গলটনে রূপান্তর করুন

আপনার অ্যাপ্লিকেশনের জীবনচক্র জুড়ে শুধুমাত্র একটি একক উদাহরণ ব্যবহার করা উচিত, অন্যথায় আপনি সমান্তরাল সংযোগ তৈরি করবেন। এটি একটি নির্ভরতা ইনজেকশন ফ্রেমওয়ার্ক যেমন ড্যাগার ব্যবহার করে করা যেতে পারে, অথবা একটি ক্লাসিক সিঙ্গেলটন প্যাটার্ন ব্যবহার করে, হয় একটি নতুন ক্লাসে বা বিদ্যমান একটিতে।

আপনি যখন কল করবেন তখন পদ্ধতিতে তৈরি না করে আপনার ক্লাসে জেনারেট করা প্রোফাইল ইন্সট্যান্স ইনজেক্ট করুন বা পাস করুন

এটি আপনাকে পরে আপনার ইউনিট পরীক্ষায় স্বয়ংক্রিয়ভাবে তৈরি FakeProfile উদাহরণে পাস করতে দেয়।

মধ্যস্থতাকারী প্যাটার্ন বিবেচনা করুন

এই সাধারণ প্যাটার্নটি হল আপনার বিদ্যমান APIগুলির মধ্যে একটি (যেমন getEvents() ) এর সমস্ত কলারদের জন্য প্রোফাইল-সচেতন করা। এই ক্ষেত্রে, আপনার বিদ্যমান API শুধুমাত্র একটি 'মধ্যস্থতাকারী' পদ্ধতি বা শ্রেণীতে পরিণত হতে পারে যাতে জেনারেট করা ক্রস-প্রোফাইল কোডে নতুন কল থাকে।

এইভাবে, আপনি প্রতিটি কলারকে ক্রস-প্রোফাইল কল করতে জানতে বাধ্য করবেন না এটি কেবল আপনার API এর অংশ হয়ে যায়।

একটি প্রদানকারীর মধ্যে আপনার বাস্তবায়নের ক্লাসগুলিকে প্রকাশ করা এড়াতে পরিবর্তে @CrossProfile হিসাবে একটি ইন্টারফেস পদ্ধতি টীকা করা যায় কিনা তা বিবেচনা করুন

এটি নির্ভরতা ইনজেকশন ফ্রেমওয়ার্কের সাথে সুন্দরভাবে কাজ করে।

আপনি যদি একটি ক্রস-প্রোফাইল কল থেকে কোনো ডেটা গ্রহণ করেন, তাহলে এটি কোন প্রোফাইল থেকে এসেছে তা উল্লেখ করে একটি ক্ষেত্র যোগ করতে হবে কিনা তা বিবেচনা করুন

এটি ভাল অনুশীলন হতে পারে কারণ আপনি এটি UI স্তরে জানতে চাইতে পারেন (যেমন কাজের জিনিসগুলিতে একটি ব্যাজ আইকন যুক্ত করা)। কোনো ডেটা শনাক্তকারী যদি এটি ছাড়া আর অনন্য না হয়, যেমন প্যাকেজ নাম।

ক্রস প্রোফাইল

এই বিভাগটি কীভাবে আপনার নিজের ক্রস প্রোফাইল ইন্টারঅ্যাকশন তৈরি করতে হয় তার রূপরেখা দেয়।

প্রাথমিক প্রোফাইল

এই ডকুমেন্টের উদাহরণের বেশিরভাগ কলে কাজের, ব্যক্তিগত এবং উভয়ই সহ কোন প্রোফাইলগুলি চালানো হবে সে সম্পর্কে স্পষ্ট নির্দেশাবলী রয়েছে৷

অনুশীলনে, শুধুমাত্র একটি প্রোফাইলে একত্রিত অভিজ্ঞতা সহ অ্যাপগুলির জন্য, আপনি সম্ভবত এই সিদ্ধান্তটি আপনি যে প্রোফাইলে চালাচ্ছেন তার উপর নির্ভর করতে চান, তাই একই ধরনের সুবিধাজনক পদ্ধতি রয়েছে যা এটিকে বিবেচনায় নেয়, যাতে আপনার কোডবেসকে নোংরা না করা যায়। if-else প্রোফাইল শর্তসাপেক্ষ।

আপনার সংযোগকারীর উদাহরণ তৈরি করার সময়, আপনি নির্দিষ্ট করতে পারেন কোন প্রোফাইলের ধরনটি আপনার 'প্রাথমিক' (যেমন 'ওয়ার্ক')। এটি অতিরিক্ত বিকল্পগুলি সক্ষম করে, যেমন নিম্নলিখিতগুলি:

profileCalendarDatabase.primary().getEvents();

profileCalendarDatabase.secondary().getEvents();

// Runs on all profiles if running on the primary, or just
// on the current profile if running on the secondary.
profileCalendarDatabase.suppliers().getEvents();

ক্রস প্রোফাইল প্রকার

@CrossProfile টীকাযুক্ত একটি পদ্ধতি রয়েছে এমন ক্লাস এবং ইন্টারফেসগুলিকে ক্রস প্রোফাইল প্রকার হিসাবে উল্লেখ করা হয়।

ক্রস প্রোফাইল প্রকারের বাস্তবায়ন প্রোফাইল-স্বাধীন হওয়া উচিত, তারা যে প্রোফাইলে চলছে। তাদের অন্যান্য পদ্ধতিতে কল করার অনুমতি দেওয়া হয় এবং সাধারণভাবে তারা একটি একক প্রোফাইলে চলমান মত কাজ করা উচিত। তারা শুধুমাত্র তাদের নিজস্ব প্রোফাইলে রাজ্য অ্যাক্সেস করতে পারবে।

একটি উদাহরণ ক্রস প্রোফাইল প্রকার:

public class Calculator {
  @CrossProfile
  public int add(int a, int b) {
    return a + b;
  }
}

ক্লাস টীকা

শক্তিশালী API প্রদান করতে, আপনাকে প্রতিটি ক্রস প্রোফাইল প্রকারের জন্য সংযোগকারী নির্দিষ্ট করতে হবে, যেমন:

@CrossProfile(connector=MyProfileConnector.class)
public class Calculator {
  @CrossProfile
  public int add(int a, int b) {
    return a + b;
  }
}

এটি ঐচ্ছিক কিন্তু এর মানে হল যে জেনারেট করা API প্রকারের ক্ষেত্রে আরও নির্দিষ্ট হবে এবং কম্পাইল-টাইম চেকিংয়ের ক্ষেত্রে কঠোর হবে।

ইন্টারফেস

@CrossProfile হিসাবে একটি ইন্টারফেসে পদ্ধতিগুলি টীকা করে আপনি বলছেন যে এই পদ্ধতির কিছু বাস্তবায়ন হতে পারে যা প্রোফাইল জুড়ে অ্যাক্সেসযোগ্য হওয়া উচিত।

আপনি একটি ক্রস প্রোফাইল প্রদানকারীতে একটি ক্রস প্রোফাইল ইন্টারফেসের যেকোনো বাস্তবায়ন ফিরিয়ে দিতে পারেন এবং এটি করার মাধ্যমে আপনি বলছেন যে এই বাস্তবায়নটি অ্যাক্সেসযোগ্য ক্রস-প্রোফাইল হওয়া উচিত। আপনি বাস্তবায়ন ক্লাস টীকা করতে হবে না.

ক্রস প্রোফাইল প্রদানকারী

প্রতিটি ক্রস প্রোফাইল টাইপ @CrossProfileProvider টীকাযুক্ত পদ্ধতি দ্বারা প্রদান করা আবশ্যক। প্রতিবার ক্রস-প্রোফাইল কল করা হলে এই পদ্ধতিগুলিকে কল করা হবে, তাই এটি সুপারিশ করা হয় যে আপনি প্রতিটি প্রকারের জন্য একক টন বজায় রাখুন।

কনস্ট্রাক্টর

একটি প্রদানকারীর অবশ্যই একটি পাবলিক কনস্ট্রাক্টর থাকতে হবে যা হয় কোন আর্গুমেন্ট বা একটি একক Context আর্গুমেন্ট নেয় না।

প্রদানকারী পদ্ধতি

প্রোভাইডার পদ্ধতিগুলিকে অবশ্যই কোন আর্গুমেন্ট বা একটি একক Context আর্গুমেন্ট নিতে হবে।

নির্ভরতা ইনজেকশন

আপনি যদি নির্ভরতা পরিচালনা করতে ড্যাগারের মতো একটি নির্ভরতা ইনজেকশন ফ্রেমওয়ার্ক ব্যবহার করেন, তাহলে আমরা সুপারিশ করব যে আপনি সেই ফ্রেমওয়ার্কটি আপনার ক্রস প্রোফাইলের প্রকারগুলি তৈরি করুন যেমন আপনি সাধারণত করেন, এবং তারপরে আপনার প্রদানকারী শ্রেণিতে এই প্রকারগুলি ইনজেকশন করুন৷ @CrossProfileProvider পদ্ধতিগুলি তারপর সেই ইনজেকশনের উদাহরণগুলি ফিরিয়ে দিতে পারে।

প্রোফাইল সংযোগকারী

প্রতিটি ক্রস প্রোফাইল কনফিগারেশনে অবশ্যই একটি একক প্রোফাইল সংযোগকারী থাকতে হবে, যা অন্য প্রোফাইলের সাথে সংযোগ পরিচালনার জন্য দায়ী৷

ডিফল্ট প্রোফাইল সংযোগকারী

যদি একটি কোডবেসে শুধুমাত্র একটি ক্রস প্রোফাইল কনফিগারেশন থাকে, তাহলে আপনি নিজের প্রোফাইল সংযোগকারী তৈরি করা এড়াতে পারেন এবং com.google.android.enterprise.connectedapps.CrossProfileConnector ব্যবহার করতে পারেন। কোনোটি নির্দিষ্ট না থাকলে এটি ব্যবহার করা ডিফল্ট।

ক্রস প্রোফাইল সংযোগকারী নির্মাণ করার সময়, আপনি নির্মাতার কিছু বিকল্প নির্দিষ্ট করতে পারেন:

  • নির্ধারিত নির্বাহক পরিষেবা

    আপনি যদি SDK দ্বারা তৈরি থ্রেডগুলির উপর নিয়ন্ত্রণ রাখতে চান তবে #setScheduledExecutorService() ব্যবহার করুন ,

  • বাইন্ডার

    প্রোফাইল বাইন্ডিং সম্পর্কিত আপনার যদি নির্দিষ্ট প্রয়োজন থাকে, তাহলে #setBinder ব্যবহার করুন। এটি সম্ভবত শুধুমাত্র ডিভাইস পলিসি কন্ট্রোলারদের দ্বারা ব্যবহৃত হয়।

কাস্টম প্রোফাইল সংযোগকারী

কিছু কনফিগারেশন ( CustomProfileConnector ব্যবহার করে) সেট করতে সক্ষম হওয়ার জন্য আপনার একটি কাস্টম প্রোফাইল সংযোগকারীর প্রয়োজন হবে এবং একটি কোডবেসে একাধিক সংযোগকারীর প্রয়োজন হলে একটির প্রয়োজন হবে (উদাহরণস্বরূপ যদি আপনার একাধিক প্রক্রিয়া থাকে, আমরা প্রতি প্রক্রিয়ায় একটি সংযোগকারীর সুপারিশ করি)।

একটি ProfileConnector তৈরি করার সময় এটি দেখতে হবে:

@GeneratedProfileConnector
public interface MyProfileConnector extends ProfileConnector {
  public static MyProfileConnector create(Context context) {
    // Configuration can be specified on the builder
    return GeneratedMyProfileConnector.builder(context).build();
  }
}
  • serviceClassName

    উত্পন্ন পরিষেবার নাম পরিবর্তন করতে (যা আপনার AndroidManifest.xml এ উল্লেখ করা উচিত), serviceClassName= ব্যবহার করুন।

  • primaryProfile

    প্রাথমিক প্রোফাইল নির্দিষ্ট করতে, primaryProfile ব্যবহার করুন।

  • availabilityRestrictions

    সংযোগ এবং প্রোফাইল উপলব্ধতার উপর SDK যে সীমাবদ্ধতা রাখে তা পরিবর্তন করতে, availabilityRestrictions ব্যবহার করুন।

ডিভাইস পলিসি কন্ট্রোলার

যদি আপনার অ্যাপটি একটি ডিভাইস পলিসি কন্ট্রোলার হয়, তাহলে আপনাকে অবশ্যই আপনার DeviceAdminReceiver এর উল্লেখ করে DpcProfileBinder এর একটি উদাহরণ উল্লেখ করতে হবে।

আপনি যদি আপনার নিজের প্রোফাইল সংযোগকারী বাস্তবায়ন করছেন:

@GeneratedProfileConnector
public interface DpcProfileConnector extends ProfileConnector {
  public static DpcProfileConnector get(Context context) {
    return GeneratedDpcProfileConnector.builder(context).setBinder(new
DpcProfileBinder(new ComponentName("com.google.testdpc",
"AdminReceiver"))).build();
  }
}

অথবা ডিফল্ট CrossProfileConnector ব্যবহার করে:

CrossProfileConnector connector =
CrossProfileConnector.builder(context).setBinder(new DpcProfileBinder(new
ComponentName("com.google.testdpc", "AdminReceiver"))).build();

ক্রস প্রোফাইল কনফিগারেশন

@CrossProfileConfiguration টীকাটি সঠিকভাবে মেথড কলগুলি পাঠানোর জন্য একটি সংযোগকারী ব্যবহার করে সমস্ত ক্রস প্রোফাইল প্রকারগুলিকে একসাথে লিঙ্ক করতে ব্যবহৃত হয়। এটি করার জন্য, আমরা @CrossProfileConfiguration সহ একটি ক্লাস টীকা করি যা প্রতিটি প্রদানকারীকে নির্দেশ করে, যেমন:

@CrossProfileConfiguration(providers = {TestProvider.class})
public abstract class TestApplication {
}

এটি যাচাই করবে যে সমস্ত ক্রস প্রোফাইলের জন্য তাদের হয় একই প্রোফাইল সংযোগকারী আছে বা কোন সংযোগকারী নির্দিষ্ট করা নেই।

  • serviceSuperclass

    ডিফল্টরূপে, জেনারেট করা পরিষেবা android.app.Service সুপারক্লাস হিসেবে ব্যবহার করবে। সুপারক্লাস হওয়ার জন্য আপনার যদি আলাদা ক্লাসের প্রয়োজন হয় (যা অবশ্যই android.app.Service এর একটি সাবক্লাস হতে হবে), তাহলে serviceSuperclass= নির্দিষ্ট করুন।

  • serviceClass

    যদি নির্দিষ্ট করা হয়, তাহলে কোনো পরিষেবা তৈরি হবে না। আপনি যে প্রোফাইল সংযোগকারীটি ব্যবহার করছেন সেটিতে এটি অবশ্যই serviceClassName সাথে মিলবে৷ আপনার কাস্টম পরিষেবাটি উত্পন্ন _Dispatcher ক্লাস ব্যবহার করে কলগুলি প্রেরণ করা উচিত যেমন:

public final class TestProfileConnector_Service extends Service {
  private Stub binder = new Stub() {
    private final TestProfileConnector_Service_Dispatcher dispatcher = new
TestProfileConnector_Service_Dispatcher();

    @Override
    public void prepareCall(long callId, int blockId, int numBytes, byte[] params)
{
      dispatcher.prepareCall(callId, blockId, numBytes, params);
    }

    @Override
    public byte[] call(long callId, int blockId, long crossProfileTypeIdentifier,
int methodIdentifier, byte[] params,
    ICrossProfileCallback callback) {
      return dispatcher.call(callId, blockId, crossProfileTypeIdentifier,
methodIdentifier, params, callback);
    }

    @Override
    public byte[] fetchResponse(long callId, int blockId) {
      return dispatcher.fetchResponse(callId, blockId);
  };

  @Override
  public Binder onBind(Intent intent) {
    return binder;
  }
}

ক্রস-প্রোফাইল কলের আগে বা পরে অতিরিক্ত ক্রিয়া সম্পাদন করার প্রয়োজন হলে এটি ব্যবহার করা যেতে পারে।

  • সংযোগকারী

    আপনি যদি ডিফল্ট CrossProfileConnector ছাড়া অন্য কোনো সংযোগকারী ব্যবহার করেন, তাহলে আপনাকে অবশ্যই connector= ব্যবহার করে এটি নির্দিষ্ট করতে হবে।

দৃশ্যমানতা

আপনার অ্যাপ্লিকেশনের প্রতিটি অংশ যা ক্রস-প্রোফাইল ইন্টারঅ্যাক্ট করে আপনার প্রোফাইল সংযোগকারীকে দেখতে সক্ষম হতে হবে।

আপনার @CrossProfileConfiguration টীকাযুক্ত ক্লাস আপনার অ্যাপ্লিকেশনে ব্যবহৃত প্রতিটি প্রদানকারীকে দেখতে সক্ষম হতে হবে।

সিঙ্ক্রোনাস কল

কানেক্টেড অ্যাপস SDK সিঙ্ক্রোনাস (ব্লকিং) কলগুলিকে সমর্থন করে যেখানে সেগুলি অনিবার্য। যাইহোক, এই কলগুলি ব্যবহার করার অনেকগুলি অসুবিধা রয়েছে (যেমন দীর্ঘ সময়ের জন্য কলগুলি ব্লক করার সম্ভাবনা) তাই এটি সুপারিশ করা হয় যে আপনি যখন সম্ভব সিঙ্ক্রোনাস কলগুলি এড়িয়ে যান ৷ অ্যাসিঙ্ক্রোনাস কল ব্যবহার করার জন্য অ্যাসিঙ্ক্রোনাস কলগুলি দেখুন।

সংযোগ ধারক

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

একটি সংযোগ ধারক যোগ করতে, যে কোনো বস্তুর সাথে ProfileConnector#addConnectionHolder(Object) কল করুন (সম্ভবত, বস্তুর উদাহরণ যা ক্রস-প্রোফাইল কল করছে)। এটি রেকর্ড করবে যে এই বস্তুটি সংযোগ ব্যবহার করছে এবং একটি সংযোগ করার চেষ্টা করবে। যেকোনো সিঙ্ক্রোনাস কল করার আগে এটি অবশ্যই কল করা উচিত। এটি একটি নন-ব্লকিং কল তাই এটি সম্ভব যে আপনি আপনার কল করার সময় সংযোগটি প্রস্তুত হবে না (বা সম্ভব নাও হতে পারে), এই ক্ষেত্রে স্বাভাবিক ত্রুটি পরিচালনার আচরণ প্রযোজ্য।

আপনি যদি ProfileConnector#addConnectionHolder(Object) কল করার সময় উপযুক্ত ক্রস-প্রোফাইল অনুমতির অভাব করেন বা সংযোগ করার জন্য কোনো প্রোফাইল উপলব্ধ না থাকে, তাহলে কোনো ত্রুটি করা হবে না কিন্তু সংযুক্ত কলব্যাককে কখনই কল করা হবে না। যদি অনুমতি পরে মঞ্জুর করা হয় বা অন্য প্রোফাইল পাওয়া যায় তাহলে সংযোগ করা হবে এবং কলব্যাক কল করা হবে।

বিকল্পভাবে, ProfileConnector#connect(Object) হল একটি ব্লকিং পদ্ধতি যা একটি সংযোগ ধারক হিসাবে বস্তুটিকে যুক্ত করবে এবং হয় একটি সংযোগ স্থাপন করবে বা একটি UnavailableProfileException নিক্ষেপ করবে। এই পদ্ধতিটি UI থ্রেড থেকে কল করা যাবে না

ProfileConnector#connect(Object) এবং অনুরূপ ProfileConnector#connect কল করা স্বয়ংক্রিয়ভাবে বন্ধ হওয়া বস্তু ফেরত দেয় যা বন্ধ হয়ে গেলে স্বয়ংক্রিয়ভাবে সংযোগ ধারককে সরিয়ে দেয়। এটি ব্যবহারের জন্য অনুমতি দেয় যেমন:

try (ProfileConnectionHolder p = connector.connect()) {
  // Use the connection
}

একবার আপনার সিঙ্ক্রোনাস কল করা শেষ হলে, আপনাকে ProfileConnector#removeConnectionHolder(Object) কল করা উচিত। একবার সমস্ত সংযোগ ধারক সরানো হলে, সংযোগটি বন্ধ হয়ে যাবে।

সংযোগ

সংযোগের অবস্থা পরিবর্তিত হলে একটি সংযোগ শ্রোতাকে জানানোর জন্য ব্যবহার করা যেতে পারে এবং সংযোগ উপস্থিত আছে কিনা তা নির্ধারণ করতে connector.utils().isConnected ব্যবহার করা যেতে পারে। যেমন:

// Only use this if using synchronous calls instead of Futures.
crossProfileConnector.connect(this);
crossProfileConnector.registerConnectionListener(() -> {
  if (crossProfileConnector.utils().isConnected()) {
    // Make cross-profile calls.
  }
});

অ্যাসিঙ্ক্রোনাস কল

প্রোফাইল বিভাজন জুড়ে প্রকাশিত প্রতিটি পদ্ধতিকে অবশ্যই ব্লকিং (সিঙ্ক্রোনাস) বা নন-ব্লকিং (অসিঙ্ক্রোনাস) হিসাবে মনোনীত করতে হবে। যে কোনও পদ্ধতি যা একটি অ্যাসিঙ্ক্রোনাস ডেটা টাইপ (যেমন একটি ListenableFuture ) প্রদান করে বা একটি কলব্যাক প্যারামিটার গ্রহণ করে তা নন-ব্লকিং হিসাবে চিহ্নিত করা হয়। অন্যান্য সমস্ত পদ্ধতি ব্লকিং হিসাবে চিহ্নিত করা হয়েছে।

অ্যাসিঙ্ক্রোনাস কলগুলি সুপারিশ করা হয়। আপনি যদি অবশ্যই সিঙ্ক্রোনাস কল ব্যবহার করেন তবে সিঙ্ক্রোনাস কলগুলি দেখুন।

কলব্যাক

নন-ব্লকিং কলের সবচেয়ে মৌলিক ধরন হল একটি অকার্যকর পদ্ধতি যা তার পরামিতিগুলির মধ্যে একটি হিসাবে গ্রহণ করে একটি ইন্টারফেস যা ফলাফল সহ কল ​​করার একটি পদ্ধতি ধারণ করে। এই ইন্টারফেসগুলিকে SDK-এর সাথে কাজ করার জন্য, ইন্টারফেসটিকে @CrossProfileCallback টীকা করতে হবে। যেমন:

@CrossProfileCallback
public interface InstallationCompleteListener {
  void installationComplete(int state);
}

এই ইন্টারফেসটি তারপর একটি @CrossProfile টীকা পদ্ধতিতে একটি প্যারামিটার হিসাবে ব্যবহার করা যেতে পারে এবং যথারীতি বলা যেতে পারে। যেমন:

@CrossProfile
public void install(String filename, InstallationCompleteListener callback) {
  // Do something on a separate thread and then:
  callback.installationComplete(1);
}

// In the mediator
profileInstaller.work().install(filename, (status) -> {
  // Deal with callback
}, (exception) -> {
  // Deal with possibility of profile unavailability
});

যদি এই ইন্টারফেসে একটি একক পদ্ধতি থাকে, যেটি হয় শূন্য বা একটি প্যারামিটার নেয়, তাহলে এটি একবারে একাধিক প্রোফাইলে কল করার ক্ষেত্রেও ব্যবহার করা যেতে পারে।

একটি কলব্যাক ব্যবহার করে যেকোনো সংখ্যক মান পাস করা যেতে পারে, তবে সংযোগটি শুধুমাত্র প্রথম মানের জন্য খোলা থাকবে। আরও মান পেতে সংযোগ খোলা রাখার বিষয়ে তথ্যের জন্য সংযোগ হোল্ডার দেখুন।

কলব্যাকের সাথে সিঙ্ক্রোনাস পদ্ধতি

SDK এর সাথে কলব্যাক ব্যবহার করার একটি অস্বাভাবিক বৈশিষ্ট্য হল যে আপনি প্রযুক্তিগতভাবে একটি সিঙ্ক্রোনাস পদ্ধতি লিখতে পারেন যা একটি কলব্যাক ব্যবহার করে:

public void install(InstallationCompleteListener callback) {
  callback.installationComplete(1);
}

এই ক্ষেত্রে, কলব্যাক সত্ত্বেও পদ্ধতিটি আসলে সিঙ্ক্রোনাস। এই কোড সঠিকভাবে চালানো হবে:

System.out.println("This prints first");
installer.install(() -> {
        System.out.println("This prints second");
});
System.out.println("This prints third");

যাইহোক, যখন SDK ব্যবহার করে কল করা হয়, তখন এটি একইভাবে আচরণ করবে না। "এই প্রিন্ট থার্ড" মুদ্রিত হওয়ার আগে ইনস্টল পদ্ধতিটি কল করা হবে এমন কোনও গ্যারান্টি নেই৷ SDK দ্বারা অ্যাসিঙ্ক্রোনাস হিসাবে চিহ্নিত কোনও পদ্ধতির যে কোনও ব্যবহার অবশ্যই কখন পদ্ধতিটি কল করা হবে সে সম্পর্কে কোনও অনুমান করা উচিত নয়।

সহজ কলব্যাক

"সাধারণ কলব্যাকগুলি" হল কলব্যাকের একটি আরও সীমাবদ্ধ ফর্ম যা ক্রস-প্রোফাইল কল করার সময় অতিরিক্ত বৈশিষ্ট্যগুলির জন্য অনুমতি দেয়৷ সাধারণ ইন্টারফেসে একটি একক পদ্ধতি থাকতে হবে, যা শূন্য বা একটি প্যারামিটার নিতে পারে।

আপনি @CrossProfileCallback টীকাতে simple=true উল্লেখ করে একটি কলব্যাক ইন্টারফেস অবশ্যই থাকতে হবে তা প্রয়োগ করতে পারেন।

সাধারণ কলব্যাকগুলি বিভিন্ন পদ্ধতি যেমন .both() , .suppliers() , এবং অন্যান্যগুলির সাথে ব্যবহারযোগ্য৷

সংযোগ ধারক

একটি অ্যাসিঙ্ক্রোনাস কল করার সময় (কলব্যাক বা ফিউচার ব্যবহার করে) কল করার সময় একটি সংযোগ ধারক যোগ করা হবে এবং একটি ব্যতিক্রম বা একটি মান পাস করা হলে তা সরানো হবে।

আপনি যদি একটি কলব্যাক ব্যবহার করে একাধিক ফলাফল পাস করার আশা করেন, তাহলে আপনাকে ম্যানুয়ালি একটি সংযোগ ধারক হিসাবে কলব্যাক যোগ করা উচিত:

MyCallback b = //...
connector.addConnectionHolder(b);

  profileMyClass.other().registerListener(b);

  // Now the connection will be held open indefinitely, once finished:
  connector.removeConnectionHolder(b);

এটি একটি ট্রাই-ওয়াথ-রিসোর্স ব্লকের সাথেও ব্যবহার করা যেতে পারে:

MyCallback b = //...
try (ProfileConnectionHolder p = connector.addConnectionHolder(b)) {
  profileMyClass.other().registerListener(b);

  // Other things running while we expect results
}

যদি আমরা একটি কলব্যাক বা ভবিষ্যতের সাথে একটি কল করি, একটি ফলাফল পাস না হওয়া পর্যন্ত সংযোগটি খোলা থাকবে। যদি আমরা নির্ধারণ করি যে একটি ফলাফল পাস করা হবে না, তাহলে আমাদের একটি সংযোগ ধারক হিসাবে কলব্যাক বা ভবিষ্যতে সরানো উচিত:

connector.removeConnectionHolder(myCallback);
connector.removeConnectionHolder(future);

আরও তথ্যের জন্য, সংযোগ হোল্ডার দেখুন।

ফিউচার

ফিউচারগুলিও SDK দ্বারা স্থানীয়ভাবে সমর্থিত। শুধুমাত্র নেটিভলি সমর্থিত ফিউচার টাইপ হল ListenableFuture , যদিও কাস্টম ফিউচার টাইপ ব্যবহার করা যেতে পারে। ফিউচার ব্যবহার করার জন্য আপনি শুধুমাত্র একটি সমর্থিত ফিউচার টাইপকে ক্রস প্রোফাইল পদ্ধতির রিটার্ন টাইপ হিসাবে ঘোষণা করুন এবং তারপরে এটি স্বাভাবিক হিসাবে ব্যবহার করুন।

এটিতে কলব্যাকগুলির মতো একই "অস্বাভাবিক বৈশিষ্ট্য" রয়েছে, যেখানে একটি সিঙ্ক্রোনাস পদ্ধতি যা একটি ভবিষ্যত প্রদান করে (যেমন immediateFuture ব্যবহার করে) অন্য প্রোফাইলে চালানোর বিপরীতে বর্তমান প্রোফাইলে চালানোর সময় ভিন্নভাবে আচরণ করবে। SDK দ্বারা অ্যাসিঙ্ক্রোনাস হিসাবে চিহ্নিত কোনও পদ্ধতির যে কোনও ব্যবহার অবশ্যই কখন পদ্ধতিটি কল করা হবে সে সম্পর্কে কোনও অনুমান করা উচিত নয়।

থ্রেড

একটি ক্রস-প্রোফাইল ভবিষ্যতের ফলাফল বা মূল থ্রেডে কলব্যাক ব্লক করবেন না আপনি যদি এটি করেন, তাহলে কিছু পরিস্থিতিতে আপনার কোড অনির্দিষ্টকালের জন্য ব্লক করবে। এর কারণ হল অন্য প্রোফাইলের সাথে সংযোগটিও মূল থ্রেডে স্থাপিত হয়, যা ক্রস-প্রোফাইলের ফলাফলের জন্য মুলতুবি থাকা অবরুদ্ধ থাকলে কখনই ঘটবে না।

প্রাপ্যতা

প্রাপ্যতা শ্রোতাকে জানানোর জন্য ব্যবহার করা যেতে পারে যখন প্রাপ্যতার অবস্থা পরিবর্তিত হয়, এবং connector.utils().isAvailable ব্যবহার করা যেতে পারে অন্য প্রোফাইল ব্যবহারের জন্য উপলব্ধ কিনা তা নির্ধারণ করতে। যেমন:

crossProfileConnector.registerAvailabilityListener(() -> {
  if (crossProfileConnector.utils().isAvailable()) {
    // Show cross-profile content
  } else {
    // Hide cross-profile content
  }
});

সংযোগ ধারক

সংযোগ ধারক হল নির্বিচারে বস্তু যা ক্রস-প্রোফাইল সংযোগ স্থাপন এবং জীবিত রাখা হচ্ছে এবং আগ্রহ হিসাবে রেকর্ড করা হয়।

ডিফল্টরূপে, অ্যাসিঙ্ক্রোনাস কল করার সময়, কল শুরু হলে একটি সংযোগ ধারক যোগ করা হবে এবং কোনো ফলাফল বা ত্রুটি ঘটলে সরানো হবে।

সংযোগ ধারকদের সংযোগের উপর আরো নিয়ন্ত্রণ প্রয়োগ করতে ম্যানুয়ালি যোগ করা এবং সরানো যেতে পারে। সংযোগ হোল্ডার connector.addConnectionHolder ব্যবহার করে যোগ করা যায় এবং connector.removeConnectionHolder ব্যবহার করে সরানো যায়।

যখন অন্তত একটি সংযোগ ধারক যোগ করা হয়, SDK একটি সংযোগ বজায় রাখার চেষ্টা করবে৷ যখন শূন্য সংযোগ ধারক যোগ করা হয়, সংযোগ বন্ধ করা যেতে পারে.

আপনার যোগ করা যেকোনো সংযোগ ধারকের একটি রেফারেন্স বজায় রাখতে হবে - এবং এটি আর প্রাসঙ্গিক না হলে এটি সরিয়ে ফেলুন।

সিঙ্ক্রোনাস কল

সিঙ্ক্রোনাস কল করার আগে, একটি সংযোগ ধারক যোগ করা উচিত। এটি যেকোন অবজেক্ট ব্যবহার করে করা যেতে পারে, যদিও আপনাকে অবশ্যই সেই অবজেক্টের ট্র্যাক রাখতে হবে যাতে আপনার আর সিঙ্ক্রোনাস কল করার প্রয়োজন না হলে এটি সরানো যায়।

অ্যাসিঙ্ক্রোনাস কল

অ্যাসিঙ্ক্রোনাস কল করার সময় সংযোগ ধারক স্বয়ংক্রিয়ভাবে পরিচালিত হবে যাতে কল এবং প্রথম প্রতিক্রিয়া বা ত্রুটির মধ্যে সংযোগটি খোলা থাকে। এর বাইরে বেঁচে থাকার জন্য যদি আপনার সংযোগের প্রয়োজন হয় (যেমন একটি একক কলব্যাক ব্যবহার করে একাধিক প্রতিক্রিয়া প্রাপ্ত করার জন্য) তাহলে আপনাকে কলব্যাকটিকে নিজেই একটি সংযোগ ধারক হিসাবে যুক্ত করতে হবে এবং একবার আপনার আর ডেটা গ্রহণের প্রয়োজন হবে না।

ত্রুটি হ্যান্ডলিং

ডিফল্টরূপে, অন্য প্রোফাইলে করা যে কোনো কল যখন অন্য প্রোফাইল উপলব্ধ না থাকে তার ফলে একটি UnavailableProfileException নিক্ষেপ করা হবে (বা ভবিষ্যতে পাস করা হবে, অথবা একটি async কলের জন্য ত্রুটি কলব্যাক)।

এটি এড়াতে, বিকাশকারীরা #both() বা #suppliers() ব্যবহার করতে পারেন এবং ফলাফল তালিকায় যেকোন সংখ্যক এন্ট্রি মোকাবেলা করতে তাদের কোড লিখতে পারেন (অন্য প্রোফাইল অনুপলব্ধ হলে এটি 1 হবে, অথবা যদি এটি উপলব্ধ থাকে 2) .

ব্যতিক্রম

বর্তমান প্রোফাইলে কল করার পরে যে কোনো অচেক করা ব্যতিক্রমগুলি যথারীতি প্রচার করা হবে। এটি কল করার জন্য ব্যবহৃত পদ্ধতি নির্বিশেষে প্রযোজ্য ( #current() , #personal , #both , ইত্যাদি)।

অন্য প্রোফাইলে কল করার পরে যে ব্যতিক্রমগুলি ঘটবে তা টিক চিহ্ন না দেওয়া হলে একটি ProfileRuntimeException কারণ হিসাবে আসল ব্যতিক্রমের সাথে নিক্ষিপ্ত হবে৷ এটি কল করার জন্য ব্যবহৃত পদ্ধতি নির্বিশেষে প্রযোজ্য ( #other() , #personal , #both , ইত্যাদি)।

যদি পাওয়া যায়

UnavailableProfileException দৃষ্টান্তগুলি ধরা এবং ডিল করার বিকল্প হিসাবে, আপনি একটি ডিফল্ট মান প্রদান করতে .ifAvailable() পদ্ধতি ব্যবহার করতে পারেন যা একটি UnavailableProfileException নিক্ষেপ করার পরিবর্তে ফেরত দেওয়া হবে।

যেমন:

profileNotesDatabase.other().ifAvailable().getNumberOfNotes(/* defaultValue= */ 0);

টেস্টিং

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

আমরা আপনার সংযোগকারীর নকল এবং প্রকার সরবরাহ করি যা পরীক্ষায় ব্যবহার করা যেতে পারে।

প্রথমে, পরীক্ষা নির্ভরতা যোগ করুন:

  testAnnotationProcessor
'com.google.android.enterprise.connectedapps:connectedapps-processor:1.1.2'
  testCompileOnly
'com.google.android.enterprise.connectedapps:connectedapps-testing-annotations:1.1.2'
  testImplementation
'com.google.android.enterprise.connectedapps:connectedapps-testing:1.1.2'

তারপরে, @CrossProfileTest এর সাথে আপনার পরীক্ষার ক্লাস টীকা করুন, @CrossProfileConfiguration টীকাযুক্ত ক্লাস পরীক্ষা করার জন্য চিহ্নিত করুন:

@CrossProfileTest(configuration = MyApplication.class)
@RunWith(RobolectricTestRunner.class)
public class NotesMediatorTest {

}

এটি কনফিগারেশনে ব্যবহৃত সমস্ত ধরণের এবং সংযোগকারীগুলির জন্য জাল তৈরির কারণ হবে৷

আপনার পরীক্ষায় সেই নকলের উদাহরণ তৈরি করুন:

private final FakeCrossProfileConnector connector = new
FakeCrossProfileConnector();
private final NotesManager personalNotesManager = new NotesManager(); //
real/mock/fake
private final NotesManager workNotesManager = new NotesManager(); // real/mock/fake

private final FakeProfileNotesManager profileNotesManager =
  FakeProfileNotesManager.builder()
    .personal(personalNotesManager)
    .work(workNotesManager)
    .connector(connector)
    .build();

প্রোফাইল অবস্থা সেট আপ করুন:

connector.setRunningOnProfile(PERSONAL);
connector.createWorkProfile();
connector.turnOffWorkProfile();

পরীক্ষার অধীনে আপনার কোডে জাল সংযোগকারী এবং ক্রস প্রোফাইল ক্লাস পাস করুন এবং তারপর কল করুন।

কলগুলি সঠিক টার্গেটে পাঠানো হবে - এবং সংযোগ বিচ্ছিন্ন বা অনুপলব্ধ প্রোফাইলগুলিতে কল করার সময় ব্যতিক্রমগুলি নিক্ষেপ করা হবে৷

সমর্থিত প্রকার

নিম্নলিখিত ধরনের আপনার পক্ষ থেকে কোন অতিরিক্ত প্রচেষ্টা ছাড়া সমর্থিত হয়. এগুলি সমস্ত ক্রস-প্রোফাইল কলের জন্য আর্গুমেন্ট বা রিটার্ন প্রকার হিসাবে ব্যবহার করা যেতে পারে।

  • আদিম ( byte , short , int , long , float , double , char , boolean ),
  • বক্সযুক্ত আদিম ( java.lang.Byte , java.lang.Short , java.lang.Integer , java.lang.Long , java.lang.Float , java.lang.Double , java.lang.Character , java.lang.Boolean , java.lang.Void ),
  • java.lang.String ,
  • যে কোনো কিছু যা android.os.Parcelable প্রয়োগ করে,
  • যে কোনো কিছু যা java.io.Serializable প্রয়োগ করে,
  • একক-মাত্রা অ-আদিম অ্যারে,
  • java.util.Optional ,
  • java.util.Collection ,
  • java.util.List ,
  • java.util.Map ,
  • java.util.Set ,
  • android.util.Pair ,
  • com.google.common.collect.ImmutableMap

যেকোন সমর্থিত জেনেরিক প্রকারের (উদাহরণস্বরূপ java.util.Collection ) তাদের টাইপ প্যারামিটার হিসাবে কোন সমর্থিত প্রকার থাকতে পারে। যেমন:

java.util.Collection<java.util.Map<java.lang.String,MySerializableType[]>> একটি বৈধ প্রকার।

ফিউচার

নিম্নলিখিত প্রকারগুলি শুধুমাত্র রিটার্ন প্রকার হিসাবে সমর্থিত:

  • com.google.common.util.concurrent.ListenableFuture

কাস্টম পার্সেলযোগ্য মোড়ক

যদি আপনার টাইপটি আগের তালিকায় না থাকে, তাহলে প্রথমে বিবেচনা করুন যে এটি android.os.Parcelable বা java.io.Serializable হয় সঠিকভাবে বাস্তবায়ন করা যায় কিনা। যদি এটি না পারে তবে আপনার প্রকারের জন্য সমর্থন যোগ করতে পার্সেলযোগ্য মোড়কগুলি দেখুন৷

কাস্টম ভবিষ্যত মোড়ক

আপনি যদি একটি ভবিষ্যত প্রকার ব্যবহার করতে চান যা পূর্বের তালিকায় নেই, তাহলে সমর্থন যোগ করতে ভবিষ্যতের মোড়কগুলি দেখুন।

পার্সেলযোগ্য মোড়ক

পার্সেলেবল র‍্যাপার হল এমন উপায় যেটি SDK অ-পার্সেলযোগ্য প্রকারের জন্য সমর্থন যোগ করে যা পরিবর্তন করা যায় না। SDK-তে অনেক ধরনের র‌্যাপার রয়েছে কিন্তু আপনার যে ধরনের ব্যবহার করতে হবে তা অন্তর্ভুক্ত না হলে আপনাকে নিজের লিখতে হবে।

একটি পার্সেলেবল র‍্যাপার হল এমন একটি শ্রেণী যা অন্য শ্রেণীকে মোড়ানো এবং এটিকে পার্সেবল করার জন্য ডিজাইন করা হয়েছে। এটি একটি সংজ্ঞায়িত স্ট্যাটিক চুক্তি অনুসরণ করে এবং SDK এর সাথে নিবন্ধিত হয় তাই এটি একটি প্রদত্ত টাইপকে একটি পার্সেলেবল টাইপে রূপান্তর করতে ব্যবহার করা যেতে পারে এবং পার্সেলেবল টাইপ থেকে সেই টাইপটি বের করতে পারে।

টীকা

পার্সেলেবল র‍্যাপার ক্লাসটি অবশ্যই @CustomParcelableWrapper টীকা করতে হবে, র‍্যাপ করা ক্লাসটিকে originalType হিসাবে উল্লেখ করে। যেমন:

@CustomParcelableWrapper(originalType=ImmutableList.class)
``` ###
Format

Parcelable wrappers must implement `Parcelable` correctly, and must have a
static `W of(Bundler, BundlerType, T)` method which wraps the wrapped type and a
non-static `T get()` method which returns the wrapped type.

The SDK will use these methods to provide seamless support for the type.

### Bundler

To allow for wrapping generic types (such as lists and maps), the `of` method is
passed a `Bundler` which is capable of reading (using `#readFromParcel`) and
writing (using `#writeToParcel`) all supported types to a `Parcel`, and a
`BundlerType` which represents the declared type to be written.

`Bundler` and `BundlerType` instances are themselves parcelable, and should be
written as part of the parcelling of the parcelable wrapper, so that it can be
used when reconstructing the parcelable wrapper.

If the `BundlerType` represents a generic type, the type variables can be found
by calling `.typeArguments()`. Each type argument is itself a `BundlerType`.

For an example, see `ParcelableCustomWrapper`:

```java
public class CustomWrapper<F> {
  private final F value;

  public CustomWrapper(F value) {
    this.value = value;
  }
  public F value() {
    return value;
  }
}

@CustomParcelableWrapper(originalType = CustomWrapper.class)
public class ParcelableCustomWrapper<E> implements Parcelable {

  private static final int NULL = -1;
  private static final int NOT_NULL = 1;

  private final Bundler bundler;
  private final BundlerType type;
  private final CustomWrapper<E> customWrapper;

  /**
  *   Create a wrapper for a given {@link CustomWrapper}.
  *
  *   <p>The passed in {@link Bundler} must be capable of bundling {@code F}.
  */
  public static <F> ParcelableCustomWrapper<F> of(
      Bundler bundler, BundlerType type, CustomWrapper<F> customWrapper) {
    return new ParcelableCustomWrapper<>(bundler, type, customWrapper);
  }

  public CustomWrapper<E> get() {
    return customWrapper;
  }

  private ParcelableCustomWrapper(
      Bundler bundler, BundlerType type, CustomWrapper<E> customWrapper) {
    if (bundler == null || type == null) {
      throw new NullPointerException();
    }
    this.bundler = bundler;
    this.type = type;
    this.customWrapper = customWrapper;
  }

  private ParcelableCustomWrapper(Parcel in) {
    bundler = in.readParcelable(Bundler.class.getClassLoader());

    int presentValue = in.readInt();

    if (presentValue == NULL) {
      type = null;
      customWrapper = null;
      return;
    }

    type = (BundlerType) in.readParcelable(Bundler.class.getClassLoader());
    BundlerType valueType = type.typeArguments().get(0);

    @SuppressWarnings("unchecked")
    E value = (E) bundler.readFromParcel(in, valueType);

    customWrapper = new CustomWrapper<>(value);
  }

  @Override
  public void writeToParcel(Parcel dest, int flags) {
    dest.writeParcelable(bundler, flags);

    if (customWrapper == null) {
      dest.writeInt(NULL);
      return;
    }

    dest.writeInt(NOT_NULL);
    dest.writeParcelable(type, flags);
    BundlerType valueType = type.typeArguments().get(0);
    bundler.writeToParcel(dest, customWrapper.value(), valueType, flags);
  }

  @Override
  public int describeContents() {
    return 0;
  }

  @SuppressWarnings("rawtypes")
  public static final Creator<ParcelableCustomWrapper> CREATOR =
    new Creator<ParcelableCustomWrapper>() {
      @Override
      public ParcelableCustomWrapper createFromParcel(Parcel in) {
        return new ParcelableCustomWrapper(in);
      }

      @Override
      public ParcelableCustomWrapper[] newArray(int size) {
        return new ParcelableCustomWrapper[size];
      }
    };
}

SDK-এর সাথে নিবন্ধন করুন

একবার তৈরি হয়ে গেলে, আপনার কাস্টম পার্সেলেবল র‍্যাপার ব্যবহার করতে আপনাকে এটি SDK-এর সাথে নিবন্ধন করতে হবে।

এটি করার জন্য, parcelableWrappers={YourParcelableWrapper.class} একটি CustomProfileConnector টীকা অথবা একটি ক্লাসে একটি CrossProfile টীকা উল্লেখ করুন।

ভবিষ্যতের মোড়ক

ফিউচার র‍্যাপার হল যেভাবে SDK প্রোফাইল জুড়ে ফিউচারের জন্য সমর্থন যোগ করে। SDK ডিফল্টরূপে ListenableFuture এর জন্য সমর্থন অন্তর্ভুক্ত করে, কিন্তু অন্যান্য ভবিষ্যতের প্রকারের জন্য আপনি নিজে সমর্থন যোগ করতে পারেন।

ফিউচার র‍্যাপার হল একটি ক্লাস যা একটি নির্দিষ্ট ফিউচার টাইপ মোড়ানো এবং এটি SDK-এর কাছে উপলব্ধ করার জন্য ডিজাইন করা হয়েছে৷ এটি একটি সংজ্ঞায়িত স্ট্যাটিক চুক্তি অনুসরণ করে এবং অবশ্যই SDK-এর সাথে নিবন্ধিত হতে হবে।

টীকা

ভবিষ্যত র‍্যাপার ক্লাসটি অবশ্যই @CustomFutureWrapper টীকা করতে হবে, র‍্যাপ করা ক্লাসটিকে originalType হিসাবে উল্লেখ করে। যেমন:

@CustomFutureWrapper(originalType=SettableFuture.class)
``` ### Format

Future wrappers must extend
`com.google.android.enterprise.connectedapps.FutureWrapper`.

Future wrappers must have a static `W create(Bundler, BundlerType)` method which
creates an instance of the wrapper. At the same time this should create an
instance of the wrapped future type. This should be returned by a non-static `T`
`getFuture()` method. The `onResult(E)` and `onException(Throwable)` methods
must be implemented to pass the result or throwable to the wrapped future.

Future wrappers must also have a static `void writeFutureResult(Bundler,`
`BundlerType, T, FutureResultWriter<E>)` method. This should register with the
passed in future for results, and when a result is given, call
`resultWriter.onSuccess(value)`. If an exception is given,
`resultWriter.onFailure(exception)` should be called.

Finally, future wrappers must also have a static `T<Map<Profile, E>>`
`groupResults(Map<Profile, T<E>> results)` method which converts a map from
profile to future, into a future of a map from profile to result.
`CrossProfileCallbackMultiMerger` can be used to make this logic easier.

For example:

```java
/** A very simple implementation of the future pattern used to test custom future
wrappers. */
public class SimpleFuture<E> {
  public static interface Consumer<E> {
    void accept(E value);
  }
  private E value;
  private Throwable thrown;
  private final CountDownLatch countDownLatch = new CountDownLatch(1);
  private Consumer<E> callback;
  private Consumer<Throwable> exceptionCallback;

  public void set(E value) {
    this.value = value;
    countDownLatch.countDown();
    if (callback != null) {
      callback.accept(value);
    }
  }

  public void setException(Throwable t) {
    this.thrown = t;
    countDownLatch.countDown();
    if (exceptionCallback != null) {
      exceptionCallback.accept(thrown);
    }
  }

  public E get() {
    try {
      countDownLatch.await();
    } catch (InterruptedException e) {
      eturn null;
    }
    if (thrown != null) {
      throw new RuntimeException(thrown);
    }
    return value;
  }

  public void setCallback(Consumer<E> callback, Consumer<Throwable>
exceptionCallback) {
    if (value != null) {
      callback.accept(value);
    } else if (thrown != null) {
      exceptionCallback.accept(thrown);
    } else {
      this.callback = callback;
      this.exceptionCallback = exceptionCallback;
    }
  }
}
/** Wrapper for adding support for {@link SimpleFuture} to the Connected Apps SDK.
*/
@CustomFutureWrapper(originalType = SimpleFuture.class)
public final class SimpleFutureWrapper<E> extends FutureWrapper<E> {

  private final SimpleFuture<E> future = new SimpleFuture<>();

  public static <E> SimpleFutureWrapper<E> create(Bundler bundler, BundlerType
bundlerType) {
    return new SimpleFutureWrapper<>(bundler, bundlerType);
  }

  private SimpleFutureWrapper(Bundler bundler, BundlerType bundlerType) {
    super(bundler, bundlerType);
  }

  public SimpleFuture<E> getFuture() {
    return future;
  }

  @Override
  public void onResult(E result) {
    future.set(result);
  }

  @Override
  public void onException(Throwable throwable) {
    future.setException(throwable);
  }

  public static <E> void writeFutureResult(
      SimpleFuture<E> future, FutureResultWriter<E> resultWriter) {

    future.setCallback(resultWriter::onSuccess, resultWriter::onFailure);
  }

  public static <E> SimpleFuture<Map<Profile, E>> groupResults(
      Map<Profile, SimpleFuture<E>> results) {
    SimpleFuture<Map<Profile, E>> m = new SimpleFuture<>();

    CrossProfileCallbackMultiMerger<E> merger =
        new CrossProfileCallbackMultiMerger<>(results.size(), m::set);
    for (Map.Entry<Profile, SimpleFuture<E>> result : results.entrySet()) {
      result
        .getValue()
        .setCallback(
          (value) -> merger.onResult(result.getKey(), value),
          (throwable) -> merger.missingResult(result.getKey()));
    }
    return m;
  }
}

SDK-এর সাথে নিবন্ধন করুন

একবার তৈরি হয়ে গেলে, আপনার কাস্টম ভবিষ্যত মোড়ক ব্যবহার করতে আপনাকে এটি SDK-এর সাথে নিবন্ধন করতে হবে।

এটি করার জন্য, একটি CustomProfileConnector টীকা বা একটি ক্লাসে একটি CrossProfile টীকাতে futureWrappers={YourFutureWrapper.class} উল্লেখ করুন।

ডাইরেক্ট বুট মোড

যদি আপনার অ্যাপ সরাসরি বুট মোড সমর্থন করে, তাহলে প্রোফাইলটি আনলক করার আগে আপনাকে ক্রস-প্রোফাইল কল করতে হতে পারে। ডিফল্টরূপে, SDK শুধুমাত্র সংযোগের অনুমতি দেয় যখন অন্য প্রোফাইলটি আনলক করা থাকে।

এই আচরণটি পরিবর্তন করতে, আপনি যদি একটি কাস্টম প্রোফাইল সংযোগকারী ব্যবহার করেন, তাহলে আপনাকে উল্লেখ করা উচিত availabilityRestrictions=AvailabilityRestrictions.DIRECT_BOOT_AWARE :

@GeneratedProfileConnector
@CustomProfileConnector(availabilityRestrictions=AvailabilityRestrictions.DIRECT_BO
OT_AWARE)
public interface MyProfileConnector extends ProfileConnector {
  public static MyProfileConnector create(Context context) {
    return GeneratedMyProfileConnector.builder(context).build();
  }
}

আপনি যদি CrossProfileConnector ব্যবহার করেন, তাহলে বিল্ডারে .setAvailabilityRestrictions(AvailabilityRestrictions.DIRECT_BOOT _AWARE ব্যবহার করুন।

এই পরিবর্তনের সাথে, আপনি উপলব্ধতা সম্পর্কে অবহিত হবেন, এবং অন্য প্রোফাইলটি আনলক না থাকলে ক্রস প্রোফাইল কল করতে পারবেন। আপনার কল শুধুমাত্র ডিভাইস এনক্রিপ্ট করা স্টোরেজ অ্যাক্সেস নিশ্চিত করার দায়িত্ব আপনার।