Search code examples
androiddebuggingloggingandroid-activityfileobserver

How to create an activity that displays a log file and observe it to refresh its layout?


I would like to display a log file that I save in the data directory of my application ("/data/data/mypackage/files/log.txt").

I need it to debug my application without eclipse.

At the moment, I display the content file in a textview, and when I want to refresh the view I click on a button that reload my textview. It is ugly but it works.

What I would like is an automatic refresh of my textview when my log file has been modified as in eclipse logcat.

So how can I do it ?

Here is what I have done :


My activity :


public class LogActivity extends Activity {

  private TextView textView;
  private String   fileName = "/data/data/my.package.app/files/log.txt";

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.log);
    Typeface tf = Typeface.createFromAsset(getAssets(), "CONSOLA.TTF");
    textView = (TextView) findViewById(R.id.textView1);
    textView.setTypeface(tf);
    setTextView();

    new FileObserver(fileName) {
        @Override
        public void onEvent(int event, String path) {
            Log.e("File", "EVENT");
            if (event == FileObserver.MODIFY) {
                Log.e("File", "MODIFY");
                setTextView();
            }
        }
    }.startWatching();
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.log_activity, menu);
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.menu_reload:
            setTextView();
            break;
        case R.id.menu_empty:
            File file = new File(fileName);
            file.delete();
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
            setTextView();
            break;
        default:
            break;
    }
    return super.onOptionsItemSelected(item);
  }

  private void setTextView() {
    try {
        FileReader fileReader = new FileReader(new File(fileName));
        BufferedReader bufferedReader = new BufferedReader(fileReader);

        String line = "";
        StringBuilder builder = new StringBuilder("");
        while ((line = bufferedReader.readLine()) != null) {
            builder.insert(0, line + "\n");
        }
        textView.setText(builder.toString());
        bufferedReader.close();

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
  }
}

My layout


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<ScrollView
android:id="@+id/SCROLLER_ID"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:fillViewport="true">
    <TextView
        android:id="@+id/textView1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" 
        />
    </ScrollView>

</LinearLayout>

My menu :


 <menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:id="@+id/menu_reload"
    android:title="Recharger"
    android:orderInCategory="100"
    android:showAsAction="always" />
  <item android:id="@+id/menu_empty"
    android:title="Vider"
    android:orderInCategory="100"
    android:showAsAction="always" />
</menu>
  • When I modify my log file (through an Android Service), I have sometimes the event that the file has been modified but not each time. why ? (if I reload manually the view through the menu, then I see that the log file has been updated).

  • The call to setTextView() inside the FileObserver is never called ?


Solution

  • Finally here is my solution (even if the Arkadiusz's one is nice).

    I have implemented a Service that is observing the file and I've registered it in my LogActivity :


    The Service :


    public class LogService extends Service {
    
    public static final String BROADCAST_FILE_LOG_UPDATE = "my.package.app.log.update";
    private String             fileName                  = "/data/data/my.package.app/files/log.txt";
    
    /**
     * The intent thanks to which is forwarded the alarm to display.
     */
    private Intent             logIntent;
    
    private FileObserver       fileObserver;
    
    @Override
    public void onCreate() {
        Log.e("LogService", "oncreate");
        logIntent = new Intent(BROADCAST_FILE_LOG_UPDATE);
        fileObserver = new FileObserver(fileName) {
            @Override
            public void onEvent(int event, String path) {
                if (event == FileObserver.MODIFY) {
                    broadcastLogUpdate();
                }
            }
        };
    }
    
    private void broadcastLogUpdate() {
        sendBroadcast(logIntent);
    }
    
    @Override
    public void onStart(Intent intent, int startid) {
        fileObserver.startWatching();
    }
    
    @Override
    public void onDestroy() {
        fileObserver.stopWatching();
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    }
    

    The LogActivity :


    public class LogActivity extends Activity {
    
    private TextView          textView;
    private String            fileName          = "/data/data/my.package.app/files/log.txt";
    
    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
                                                    @Override
                                                    public void onReceive(Context context, Intent intent) {
                                                        setTextView();
                                                    }
                                                };
    private Intent            serviceIntent;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        serviceIntent = new Intent(getApplicationContext(), LogService.class);
        setContentView(R.layout.log);
        Typeface tf = Typeface.createFromAsset(getAssets(), "CONSOLA.TTF");
        textView = (TextView) findViewById(R.id.textView1);
        textView.setTypeface(tf);
        setTextView();
    }
    
    @Override
    protected void onResume() {
        super.onResume();
        try {
            startService(serviceIntent);
            registerReceiver(broadcastReceiver, new IntentFilter(LogService.BROADCAST_FILE_LOG_UPDATE));
        } catch (IllegalArgumentException e) {}
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        try {
            unregisterReceiver(broadcastReceiver);
        } catch (IllegalArgumentException e) {}
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.log_activity, menu);
        return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_reload:
                Toast.makeText(getApplicationContext(), "reload", Toast.LENGTH_LONG).show();
                setTextView();
                break;
            case R.id.menu_empty:
                new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert)
                        .setTitle("Erase log file ?").setMessage("Sure ?")
                        .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                File file = new File(fileName);
                                file.delete();
                                try {
                                    file.createNewFile();
                                    unregisterReceiver(broadcastReceiver);
                                    stopService(serviceIntent);
                                    startService(serviceIntent);
                                    registerReceiver(broadcastReceiver, new IntentFilter(
                                            LogService.BROADCAST_FILE_LOG_UPDATE));
    
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                                setTextView();
                            }
    
                        }).setNegativeButton("No", null).show();
                break;
            default:
                break;
        }
        return super.onOptionsItemSelected(item);
    }
    
    private void setTextView() {
        try {
            FileReader fileReader = new FileReader(new File(fileName));
            BufferedReader bufferedReader = new BufferedReader(fileReader);
    
            String line = "";
            StringBuilder builder = new StringBuilder("");
            while ((line = bufferedReader.readLine()) != null) {
                builder.insert(0, line + "\n");
            }
            textView.setText(builder.toString());
            bufferedReader.close();
    
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    }
    

    Of course, don't forget to add the new service in the manifest.


    The manifest :


        <service android:name=".LogService" />
    

    It works very well.

    I hope it would be useful for somebody else.