অপ্রয়োজনীয় ব্যতিক্রম পরিচালনা করতে এবং লগ ফাইলটি প্রেরণ করা দরকার


114

আপডেট: দয়া করে নীচে "স্বীকৃত" সমাধান দেখুন

যখন আমার অ্যাপ্লিকেশনটি কেবলমাত্র সমাপ্তির পরিবর্তে একটি অযথিত ব্যতিক্রম তৈরি করে, আমি প্রথমে ব্যবহারকারীকে লগ ফাইল প্রেরণের সুযোগ দিতে চাই। আমি বুঝতে পেরেছি যে এলোমেলো ব্যতিক্রম পাওয়ার পরে আরও কাজ করা ঝুঁকিপূর্ণ তবে, আরে, অ্যাপটি ক্র্যাশ করে শেষ করে দেয় এবং লগ ফাইলটি প্রেরণ হয় না the এটি আমার প্রত্যাশার চেয়ে কৌশলযুক্ত হয়ে উঠছে :)

কী কাজ করে: (1) অবিরত ব্যতিক্রমকে ফাঁদে ফেলা, (২) লগের তথ্য বের করা এবং কোনও ফাইলে লেখা।

এখনও কী কাজ করে না: (3) ইমেল প্রেরণের জন্য কোনও ক্রিয়াকলাপ শুরু করা। শেষ পর্যন্ত, আমি ব্যবহারকারীর অনুমতি জিজ্ঞাসা করার জন্য আরও একটি ক্রিয়াকলাপ করব। আমি যদি ইমেল ক্রিয়াকলাপটি কাজ করে দেখি তবে আমি অন্যটির জন্য খুব বেশি সমস্যা আশা করি না।

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

কোন পরামর্শ কিভাবে এই কাজ করতে?

শুরুর দিক নির্দেশিকা হিসাবে এখানে কিছু কোড স্নিপ রয়েছে:

public class MyApplication extends Application
{
  defaultUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();
  public void onCreate ()
  {
    Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler()
    {
      @Override
      public void uncaughtException (Thread thread, Throwable e)
      {
        handleUncaughtException (thread, e);
      }
    });
  }

  private void handleUncaughtException (Thread thread, Throwable e)
  {
    String fullFileName = extractLogToFile(); // code not shown

    // The following shows what I'd like, though it won't work like this.
    Intent intent = new Intent (Intent.ACTION_SEND);
    intent.setType ("plain/text");
    intent.putExtra (Intent.EXTRA_EMAIL, new String[] {"me@mydomain.com"});
    intent.putExtra (Intent.EXTRA_SUBJECT, "log file");
    intent.putExtra (Intent.EXTRA_STREAM, Uri.parse ("file://" + fullFileName));
    startActivityForResult (intent, ACTIVITY_REQUEST_SEND_LOG);
  }

  public void onActivityResult (int requestCode, int resultCode, Intent data)
  {
    if (requestCode == ACTIVITY_REQUEST_SEND_LOG)
      System.exit(1);
  }
}

4
ব্যক্তিগতভাবে আমি কেবল এসিআরএ ব্যবহার করি , যদিও এটি ওপেন সোর্স তাই আপনি কীভাবে এটি পরীক্ষা করে দেখতে পারেন ...
ডেভিড ও'মায়েরা

উত্তর:


240

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

আপনার অনেকগুলি কাজ করতে হবে:

  1. আপনার অ্যাপ্লিকেশন সাবক্লাসে অনুচ্চারিত ধারণা গ্রহণ করুন।
  2. একটি ব্যতিক্রম ধরা পরে, ব্যবহারকারীকে লগ পাঠাতে বলার জন্য একটি নতুন ক্রিয়াকলাপ শুরু করুন।
  3. লগক্যাটের ফাইলগুলি থেকে লগ তথ্যটি বের করুন এবং নিজের ফাইলটিতে লিখুন।
  4. সংযুক্তি হিসাবে আপনার ফাইল সরবরাহ করে একটি ইমেল অ্যাপ্লিকেশন শুরু করুন।
  5. ম্যানিফেস্ট: আপনার ব্যতিক্রম হ্যান্ডলার দ্বারা স্বীকৃত হতে আপনার কার্যকলাপ ফিল্টার করুন filter
  6. Allyচ্ছিকভাবে, লগ.ডি () এবং লগ.ভি () কে সরিয়ে ফেলার জন্য প্রগার্ড সেটআপ করুন।

এখন, বিশদটি এখানে:

(1 এবং 2) অবিচ্ছিন্ন ধারণা গ্রহণ করুন, লগ কার্যকলাপ প্রেরণ শুরু করুন:

public class MyApplication extends Application
{
  public void onCreate ()
  {
    // Setup handler for uncaught exceptions.
    Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler()
    {
      @Override
      public void uncaughtException (Thread thread, Throwable e)
      {
        handleUncaughtException (thread, e);
      }
    });
  }

  public void handleUncaughtException (Thread thread, Throwable e)
  {
    e.printStackTrace(); // not all Android versions will print the stack trace automatically

    Intent intent = new Intent ();
    intent.setAction ("com.mydomain.SEND_LOG"); // see step 5.
    intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK); // required when starting from Application
    startActivity (intent);

    System.exit(1); // kill off the crashed app
  }
}

(3) লগ এক্সট্রাক্ট করুন (আমি এটি আমার সেন্ডলগ ক্রিয়াকলাপটি রেখেছি):

private String extractLogToFile()
{
  PackageManager manager = this.getPackageManager();
  PackageInfo info = null;
  try {
    info = manager.getPackageInfo (this.getPackageName(), 0);
  } catch (NameNotFoundException e2) {
  }
  String model = Build.MODEL;
  if (!model.startsWith(Build.MANUFACTURER))
    model = Build.MANUFACTURER + " " + model;

  // Make file name - file must be saved to external storage or it wont be readable by
  // the email app.
  String path = Environment.getExternalStorageDirectory() + "/" + "MyApp/";
  String fullName = path + <some name>;

  // Extract to file.
  File file = new File (fullName);
  InputStreamReader reader = null;
  FileWriter writer = null;
  try
  {
    // For Android 4.0 and earlier, you will get all app's log output, so filter it to
    // mostly limit it to your app's output.  In later versions, the filtering isn't needed.
    String cmd = (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) ?
                  "logcat -d -v time MyApp:v dalvikvm:v System.err:v *:s" :
                  "logcat -d -v time";

    // get input stream
    Process process = Runtime.getRuntime().exec(cmd);
    reader = new InputStreamReader (process.getInputStream());

    // write output stream
    writer = new FileWriter (file);
    writer.write ("Android version: " +  Build.VERSION.SDK_INT + "\n");
    writer.write ("Device: " + model + "\n");
    writer.write ("App version: " + (info == null ? "(null)" : info.versionCode) + "\n");

    char[] buffer = new char[10000];
    do 
    {
      int n = reader.read (buffer, 0, buffer.length);
      if (n == -1)
        break;
      writer.write (buffer, 0, n);
    } while (true);

    reader.close();
    writer.close();
  }
  catch (IOException e)
  {
    if (writer != null)
      try {
        writer.close();
      } catch (IOException e1) {
      }
    if (reader != null)
      try {
        reader.close();
      } catch (IOException e1) {
      }

    // You might want to write a failure message to the log here.
    return null;
  }

  return fullName;
}

(4) একটি ইমেল অ্যাপ্লিকেশন শুরু করুন (আমার সেন্ডলগ ক্রিয়াকলাপেও):

private void sendLogFile ()
{
  String fullName = extractLogToFile();
  if (fullName == null)
    return;

  Intent intent = new Intent (Intent.ACTION_SEND);
  intent.setType ("plain/text");
  intent.putExtra (Intent.EXTRA_EMAIL, new String[] {"log@mydomain.com"});
  intent.putExtra (Intent.EXTRA_SUBJECT, "MyApp log file");
  intent.putExtra (Intent.EXTRA_STREAM, Uri.parse ("file://" + fullName));
  intent.putExtra (Intent.EXTRA_TEXT, "Log file attached."); // do this so some email clients don't complain about empty body.
  startActivity (intent);
}

(3 এবং 4) এখানে সেন্ডলগ দেখতে কেমন দেখাচ্ছে (যদিও আপনাকে ইউআই যোগ করতে হবে):

public class SendLog extends Activity implements OnClickListener
{
  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    requestWindowFeature (Window.FEATURE_NO_TITLE); // make a dialog without a titlebar
    setFinishOnTouchOutside (false); // prevent users from dismissing the dialog by tapping outside
    setContentView (R.layout.send_log);
  }

  @Override
  public void onClick (View v) 
  {
    // respond to button clicks in your UI
  }

  private void sendLogFile ()
  {
    // method as shown above
  }

  private String extractLogToFile()
  {
    // method as shown above
  }
}

(5) প্রকাশ:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
    <!-- needed for Android 4.0.x and eariler -->
    <uses-permission android:name="android.permission.READ_LOGS" /> 

    <application ... >
        <activity
            android:name="com.mydomain.SendLog"
            android:theme="@android:style/Theme.Dialog"
            android:textAppearance="@android:style/TextAppearance.Large"
            android:windowSoftInputMode="stateHidden">
            <intent-filter>
              <action android:name="com.mydomain.SEND_LOG" />
              <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
     </application>
</manifest>

()) সেটআপ প্রগার্ড:

প্রজেক্ট.প্রোপার্টিগুলিতে কনফিগার লাইনটি পরিবর্তন করুন। আপনাকে অবশ্যই "অনুকূলিতকরণ" নির্দিষ্ট করতে হবে বা প্রোগার্ড লগ.ভ () এবং লগ.ড () কলগুলি সরিয়ে ফেলবে না

proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt

প্রগুয়ার্ড-প্রজেক্ট.টেক্সটে নিম্নলিখিতটি যুক্ত করুন। এটি প্রোগ্রার্ডকে ধরে নিতে বলুন লগ.ভি এবং লগ.ডের কোনও পার্শ্ব প্রতিক্রিয়া নেই (যদিও তারা লগগুলিতে লেখার পরে তা করে) এবং এটি অপ্টিমাইজেশনের সময় অপসারণ করা যেতে পারে:

-assumenosideeffects class android.util.Log {
    public static int v(...);
    public static int d(...);
}

এটাই! আপনার যদি এর উন্নতির জন্য কোনও পরামর্শ থাকে তবে দয়া করে আমাকে জানান এবং আমি এটি আপডেট করতে পারি।


10
কেবল একটি নোট: কখনও System.exit কল করবেন না। আপনি অপ্রকাশিত ব্যতিক্রম হ্যান্ডলারের শৃঙ্খলা ভঙ্গ করবেন। একে একে পরের দিকে ফরোয়ার্ড করুন। আপনার ইতিমধ্যে getDefaultUncaughtExceptionHandler থেকে "ডিফল্টআউটচ্যাট্যান্ড হ্যান্ডলার" রয়েছে, আপনি যখন কাজটি সম্পন্ন করবেন তখন কেবলমাত্র কলটি কল করুন।
গিলম

2
@ গিল্ম, আপনি কীভাবে "একে একে পরবর্তীকে" ফরোয়ার্ড করবেন এবং হ্যান্ডলার শৃঙ্খলে আর কী ঘটতে পারে তার উদাহরণ দিতে পারেন? এটি বেশ কিছুক্ষণ হয়েছে, তবে আমি বেশ কয়েকটি পরিস্থিতি পরীক্ষা করে দেখেছি এবং System.exit () কে কল করা সেরা সমাধান বলে মনে হচ্ছে। সর্বোপরি, অ্যাপ্লিকেশনটি ক্র্যাশ হয়ে গেছে এবং এটি বন্ধ হওয়া দরকার।
পেরি হার্টম্যান

আমার মনে হয় আপনি যদি অপ্রকাশিত ব্যতিক্রম চালিয়ে যেতে দেন তবে কী ঘটে তা আমি মনে করি: সিস্টেমটি "অ্যাপ্লিকেশন সমাপ্ত" বিজ্ঞপ্তি দেয়। সাধারণত, এটা ঠিক আছে। তবে যেহেতু নতুন ক্রিয়াকলাপটি (এটি লগ সহ ইমেল প্রেরণ করে) ইতিমধ্যে নিজস্ব বার্তা পেশ করে চলেছে, সিস্টেমটিকে অন্য একটি স্থাপন করা বিভ্রান্তিকর। সুতরাং আমি এটিকে System.exit () দিয়ে নিঃশব্দে বাতিল করতে বাধ্য করি।
পেরি হার্টম্যান

8
@ পেরি হার্টম্যান নিশ্চিত: কেবলমাত্র একটি ডিফল্ট হ্যান্ডলার রয়েছে। ডিফল্টউনকুটএক্সপেনস হ্যান্ডলার () কল করার আগে আপনাকে অবশ্যই getDefaultUncaughtExceptionHandler () কল করতে হবে এবং সেই রেফারেন্সটি রাখতে হবে। সুতরাং, বলুন যে আপনি বাগসনেস করেছেন, ক্র্যাশলিটিক্স এবং আপনার হ্যান্ডলারটি সর্বশেষ ইনস্টলড রয়েছে, সিস্টেমটি কেবল আপনাকে কল করবে। আপনি getDefaultUncaughtExceptionHandler () এর মাধ্যমে যে রেফারেন্সটি পেয়েছেন তা কল করা এবং থ্রেডটি পাস এবং শৃঙ্খলে পরবর্তী স্থানে ফেলে দেওয়া। আপনি যদি কেবল System.exit () এ চলে যান তবে অন্যকে ডাকা হবে না।
গিলম

4
আপনাকে ম্যানিফেস্টে অ্যাপ্লিকেশন ট্যাগের একটি বৈশিষ্ট্য সহ অ্যাপ্লিকেশনটির বাস্তবায়নও নিবন্ধিত করতে হবে, যেমন: <অ্যাপ্লিকেশন অ্যান্ড্রয়েড: নাম = "com.mydomain.Myapplication" অন্যান্য অ্যাটর্স ... />
ম্যাট

9

আজ অনেকগুলি ক্র্যাশ রিপ্রটিং সরঞ্জাম রয়েছে যা সহজেই এটি করে।

  1. ক্র্যাশলিটিক্স - একটি ক্র্যাশ রিপোর্টিং সরঞ্জাম, নিখরচায় তবে আপনাকে প্রাথমিক প্রতিবেদনগুলি সুবিধা দেয়: বিনামূল্যে

  2. গ্রিফোনেট - আরও উন্নত প্রতিবেদনের সরঞ্জাম, এক ধরণের ফি প্রয়োজন requires সুবিধা: ক্র্যাশগুলির সহজ বিনোদন, এএনআর, স্লোনেস ...

আপনি যদি বেসরকারী বিকাশকারী হন তবে আমি ক্র্যাশলিটিক্সের পরামর্শ দেব, তবে এটি যদি একটি বড় সংগঠন হয় তবে আমি গ্রিফোনেট যাব।

শুভকামনা!


5

পরিবর্তে ACRA ব্যবহার করে দেখুন - এটি স্ট্যাক ট্রেস পাশাপাশি আপনার ব্যাকএন্ডে বা আপনার সেট আপ করা Google ডক্স নথিতে প্রচুর অন্যান্য দরকারী ডিবাগ তথ্য প্রেরণকে পরিচালনা করে।

https://github.com/ACRA/acra


5

@ পেরি হার্টম্যানের উত্তর যখন ইউআই থ্রেডটি বাদ দিয়ে ব্যতিক্রম ছুঁড়ে ফেলেছে তখন ভালভাবে কাজ করে। যখন অনাবৃত ব্যতিক্রম কোনও ইউআই থ্রেড দ্বারা নিক্ষেপ করা হয় তখন আমি কিছু উন্নতি করেছি।

public boolean isUIThread(){
    return Looper.getMainLooper().getThread() == Thread.currentThread();
}

public void handleUncaughtException(Thread thread, Throwable e) {
    e.printStackTrace(); // not all Android versions will print the stack trace automatically

    if(isUIThread()) {
        invokeLogActivity();
    }else{  //handle non UI thread throw uncaught exception

        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                invokeLogActivity();
            }
        });
    }
}

private void invokeLogActivity(){
    Intent intent = new Intent ();
    intent.setAction ("com.mydomain.SEND_LOG"); // see step 5.
    intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK); // required when starting from Application
    startActivity (intent);

    System.exit(1); // kill off the crashed app
}

2

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

String[] cmd = new String[] {"logcat","-f",filePath,"-v","time","<MyTagName>:D","*:S"};
        try {
            Runtime.getRuntime().exec(cmd);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

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


আমি বিশ্বাস করি যে আমি চেষ্টা করেছি (বা এরকম কিছু)। যদি আমি মনে করি, আমি অনুমতি সমস্যার মধ্যে দৌড়েছি। আপনি কি এটি কোনও মূলযুক্ত ডিভাইসে করছেন?
পেরি হার্টম্যান

ওহ, আমি আপনাকে বিভ্রান্ত করলে আমি দুঃখিত, তবে আমি এখনও আমার অনুকরণকারীদের সাথে চেষ্টা করছি। আমি এখনও এটি কোনও ডিভাইসে পোর্ট করতে পারছি না (তবে হ্যাঁ আমি পরীক্ষার জন্য যে ডিভাইসটি ব্যবহার করি এটি একটি মূলযুক্ত)।
স্কো করুন

2

অপ্রকাশিত ব্যতিক্রমগুলি হ্যান্ডলিং: @ গিলম যেমন ব্যাখ্যা করেছেন ঠিক তেমন করুন, (কোটলিন):

private val defaultUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();

override fun onCreate() {
  //...
    Thread.setDefaultUncaughtExceptionHandler { t, e ->
        Crashlytics.logException(e)
        defaultUncaughtHandler?.uncaughtException(t, e)
    }
}

আমি আশা করি এটি সাহায্য করে, এটি আমার পক্ষে কাজ করেছে .. (: y)। আমার ক্ষেত্রে আমি ত্রুটি সনাক্তকরণের জন্য 'com.microsoft.appcenter.crashes.Crashes' লাইব্রেরি ব্যবহার করেছি।


0

আপনি ফায়ার ক্র্যাশার ব্যবহার করে অনাবৃত ব্যতিক্রমগুলি পরিচালনা করতে পারেন লাইব্রেরি এবং এটি থেকে পুনরুদ্ধার করতে পারেন।

আপনি এই মাঝারি নিবন্ধটিতে লাইব্রেরি সম্পর্কে আরও জানতে পারবেন

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.