пятница, 28 сентября 2012 г.

Скелет для Activity


Скелет для Activity
Во время разработки игр и приложений часто возникают ситуации, когда приходиться в каждом разработанном Activity писать один и тот же код. Потом переносить его в другие проекты. В этой статье я хотел бы рассказать о том, как можно облегчить жизнь при создании очередного Activity в приложении.

Теория


Что чаще всего мы используем при создании нового Activity? Конечно это установка отображаемого View (метод setContentView()), отправка статистики, использование Wake Lock, логирование методов, запуск и остановка музыки и много другое. Почему бы все эти методы не перенести в один класс, и затем наследоваться от него в нашем Activity.

Практика

Разработаем абстрактный класс: SceletonActivity. Наследуемся от android.app.Activity, чтобы можно было использовать соответствующие методы.
public abstract class SceletonActivity extends Activity {
}
В этой статье я добавлю для нашего «скелета» возможность отследить паузу приложения, использование Wake Lock, логирование и отправку статистики.
Добавим основные используемые методы в наш Activity. Для логирования я использую класс, описанный мною в статье «Логирование в Android». Так же добавим атрибут protected String tag. Он будет использоваться для хранения текущего имени класса.
    protected String tag;  // Имя класса для логирования

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LogSystem.w(tag, "onCreate()");
   }
    
    @Override
 protected void onPause() {
  super.onPause();
  LogSystem.w(tag, "onPause()");
 }
 
 @Override
 protected void onResume() {
  super.onResume();
  LogSystem.w(tag, "onResume()");
 } 
  
 @Override
 protected void onDestroy() {
  super.onDestroy();
  LogSystem.w(tag, "onDestroy()");
 }

 @Override
 protected void onStart() {
  super.onStart();
  LogSystem.w(tag, "onStart()");
 }

 @Override
 protected void onStop() {
  super.onStop();
  LogSystem.w(tag, "onStop()");
 }
Следующий этап разработки нашего «скелета» - отслеживание паузы в приложении. Для этого мы добавим атрибут protected boolean mPaused. И немного изменим наши методы:
 protected boolean mPaused; // отслеживание паузы в приложении
 @Override
 protected void onPause() {
  super.onPause();
  LogSystem.w(tag, "onPause()");
  this.mPaused = true;
 }
 
 @Override
 protected void onResume() {
  super.onResume();
  LogSystem.w(tag, "onResume()");
  this.mPaused = false;
 }

Для чего можно использовать этот атрибут я покажу чуть позже в этой статье.
Теперь нам необходимо установить для Activity соответствующее View и Tag, а также инициализировать все элементы UI нашего Activity. Для этого у нас будут использоваться абстрактные методы:
protected abstract void initInterface(); // Инициализация интерфейса
protected abstract void setAttr(); // Установка атрибутов класса
Их реализация заложена в тех Activity, которые будут наследниками для SceletonActivity. Вызов этих методов необходимо поместить в метод onCreate() нашего «скелета»:
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        
        setAttr(); 
        LogSystem.w(tag, "onCreate()");        
        setContentView(view);
        initInterface();
    }
Так же мы хотим отправлять статистику на используемый нами сервис (например,  Google Analitics или Flurry). Для этого мы добавим следующий метод:
public void trackSupportedApp(String activity,  Map map) {
  LogSystem.w(tag, "trackSupportedApp(" + activity + ")");
    }
Вместо логирования необходимо добавит код отправки статистики для используемого вами сервиса. Например, для Flurry код может выглядеть следующим образом:
public static void trackSupportedApp(String activity,  Map map) {
  if (activity == null || activity.length() == 0) return;
        if (map == null)
         FlurryAgent.logEvent(activity);
        else
         FlurryAgent.logEvent(activity, map);
    }
И на последок я добавлю WakeLock, чтобы наше приложение не отключало экран во время работы.
Для этого нам необходимо 3 метода:
  /**
  * Инициализация WakeLock
  */
 private void initWl()
 {
  if (wl == null)
        {
         PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
   wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
     | PowerManager.ACQUIRE_CAUSES_WAKEUP, "org.snowpard.projects.five"); 
        }
 }
 
 /**
  * Запускаем выполнение запрошенных функций WakeLock
  */
 public void wlAsquire()
 {
  try 
  {
   if (wl != null)
    wl.acquire();
  }catch (Exception e) {
  }
 }
 
 /**
  * Останавливаем выполнение запрошенных функций WakeLock
  */
 public void wlRelease()
 {
  try 
  {
   if (wl != null)
    wl.release();
  }catch (Exception e) {
  }
 }
Снова изменим наши методы для использования новых функций:
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        setAttr(); 
        LogSystem.w(tag, "onCreate()");
        
        setContentView(view);
        trackSupportedApp(tag, null);
        initWl();
        initInterface();
    }
    
    @Override
 protected void onPause() {
  super.onPause();
  LogSystem.w(tag, "onPause()");
  this.mPaused = true;
  wlRelease();
 }
 
 @Override
 protected void onResume() {
  super.onResume();
  LogSystem.w(tag, "onResume()");
  this.mPaused = false;
  wlAsquire();
 }

Использование SceletonActivity

Теперь я покажу, как упростилась работы при разработке наших Activity. Разработаем приложение, содержащее 2 Activity и кнопки для перемещения между ними.
Пример файла strings.xml
<resources> 
    <string name="btn_next">Следующее Activity</string>
    <string name="btn_previous">Предыдущее Activity</string>
    <string name="app_name">Skeleton Example</string> 
</resources>
Пример файлов макетов для наших Activity:
first.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" > 
    <Button
        android:id="@+id/btn_next"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/btn_next" /> 
</LinearLayout>

second.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" > 
    <Button
        android:id="@+id/btn_previous"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/btn_previous" /> 
</LinearLayout>
И сами классы:
FirstActivity.java
public class FirstActivity extends SceletonActivity {

 @Override
 protected void initInterface() {
  Button btn = (Button)findViewById(R.id.btn_next);
  btn.setOnClickListener(new OnClickListener() {
   
   @Override
   public void onClick(View v) {
    Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
    startActivity(intent);    
   }
  });
 }

 @Override
 protected void setAttr() {
  this.tag = FirstActivity.class.getSimpleName();  
  this.view = R.layout.first;
 }

}
>

SecondActivity.java
public class SecondActivity extends SceletonActivity {

 @Override
 protected void initInterface() {
  Button btn = (Button)findViewById(R.id.btn_previous);
  btn.setOnClickListener(new OnClickListener() {
   
   @Override
   public void onClick(View v) {
    finish();   
   }
  });
 }

 @Override
 protected void setAttr() {
  this.tag = SecondActivity.class.getSimpleName();  
  this.view = R.layout.second;
 }

}
Как видите, повторяющегося кода нет, сам код достаточно компактен, а наши Activity многофункциональны.
Не забудьте добавит созданные Activity в AndroidManifest:
        <activity
            android:name=".FirstActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".SecondActivity"
            android:label="@string/app_name" >
        </activity>

Атрибут mPaused

Как я и обещал, расскажу немного об атрибуте mPaused, который используется для отслеживания паузы в приложении. Как его можно использовать? Бывает, что при нажатии на кнопку, действие происходит не сразу (девайсы разные бывают), и пользователь может произвести повторное нажатие, которое, например, было бы в вашем приложении не желательно. Для этого и используется mPaused. Пример использования:
btn.setOnClickListener(new OnClickListener() {
   
   @Override
   public void onClick(View v) {
    if (!mPaused)
    {
     Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
     startActivity(intent);
     mPaused = true;
    }
   }
  });
  

Ссылки

Исходные коды данного проекта можно скачать отсюда: zip

Комментариев нет:

Отправить комментарий