你的位置:首页 > 操作系统

[操作系统][Android]开源中国源码分析之一


开源中国android端版本号:2.4

启动界面:

Screenshot_20160226-171547

在AndroidManifest.

 <activity
      android:name=".AppStart"
      android:label="@string/app_name"
      android:screenOrientation="portrait"
      android:theme="@style/Theme.AppStartLoad" >
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>

屏幕方向设置为竖屏,主题为AppStartLoad,关于它的定义如下:

 <style name="Theme.AppStartLoad" parent="android:Theme.Black.NoTitleBar.Fullscreen">
    <item name="android:windowBackground">@drawable/welcome</item>
    <item name="android:windowNoTitle">true</item>
  </style>

welcome.png就是启动页显示的图片,填充了整个屏幕。

AppStart.java文件:

package net.oschina.app;
import java.io.File;
import net.oschina.app.ui.MainActivity;
import net.oschina.app.util.TDevice;
import org.kymjs.kjframe.http.KJAsyncTask;
import org.kymjs.kjframe.utils.FileUtils;
import org.kymjs.kjframe.utils.PreferenceHelper;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
/**
 * 应用启动界面
 * 
 * @author FireAnt(http://my.oschina.net/LittleDY)
 * @created 2014年12月22日 上午11:51:56
 * 
 */
public class AppStart extends Activity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 防止第三方跳转时出现双实例
    Activity aty = AppManager.getActivity(MainActivity.class);
    if (aty != null && !aty.isFinishing()) {
      finish();
    }
    // SystemTool.gc(this); //针对性能好的手机使用,加快应用相应速度
    final View view = View.inflate(this, R.layout.app_start, null);
    setContentView(view);
    // 渐变展示启动屏
    AlphaAnimation aa = new AlphaAnimation(0.5f, 1.0f);
    aa.setDuration(800);
    view.startAnimation(aa);
    aa.setAnimationListener(new AnimationListener() {
      @Override
      public void onAnimationEnd(Animation arg0) {
        redirectTo();
      }
      @Override
      public void onAnimationRepeat(Animation animation) {}
      @Override
      public void onAnimationStart(Animation animation) {}
    });
  }
  @Override
  protected void onResume() {
    super.onResume();
    int cacheVersion = PreferenceHelper.readInt(this, "first_install",
        "first_install", -1);
    int currentVersion = TDevice.getVersionCode();
    if (cacheVersion < currentVersion) {
      PreferenceHelper.write(this, "first_install", "first_install",
          currentVersion);
      cleanImageCache();
    }
  }
  private void cleanImageCache() {
    final File folder = FileUtils.getSaveFolder("OSChina/imagecache");
    KJAsyncTask.execute(new Runnable() {
      @Override
      public void run() {
        for (File file : folder.listFiles()) {
          file.delete();
        }
      }
    });
  }
  /**
   * 跳转到...
   */
  private void redirectTo() {
    Intent uploadLog = new Intent(this, LogUploadService.class);
    startService(uploadLog);
    Intent intent = new Intent(this, MainActivity.class);
    startActivity(intent);
    finish();
  }
}

AppManager.java文件:

package net.oschina.app;
import java.util.Stack;
import android.app.Activity;
import android.content.Context;
/**
 * activity堆栈式管理
 *
 * @author FireAnt(http://my.oschina.net/LittleDY)
 * @created 2014年10月30日 下午6:22:05
 *
 */
public class AppManager {
  private static Stack<Activity> activityStack;
  private static AppManager instance;
  private AppManager() {}
  /**
   * 单一实例
   */
  public static AppManager getAppManager() {
    if (instance == null) {
      instance = new AppManager();
    }
    return instance;
  }
  /**
   * 添加Activity到堆栈
   */
  public void addActivity(Activity activity) {
    if (activityStack == null) {
      activityStack = new Stack<Activity>();
    }
    activityStack.add(activity);
  }
  /**
   * 获取当前Activity(堆栈中最后一个压入的)
   */
  public Activity currentActivity() {
    Activity activity = activityStack.lastElement();
    return activity;
  }
  /**
   * 结束当前Activity(堆栈中最后一个压入的)
   */
  public void finishActivity() {
    Activity activity = activityStack.lastElement();
    finishActivity(activity);
  }
  /**
   * 结束指定的Activity
   */
  public void finishActivity(Activity activity) {
    if (activity != null && !activity.isFinishing()) {
      activityStack.remove(activity);
      activity.finish();
      activity = null;
    }
  }
  /**
   * 结束指定类名的Activity
   */
  public void finishActivity(Class<?> cls) {
    for (Activity activity : activityStack) {
      if (activity.getClass().equals(cls)) {
        finishActivity(activity);
        break;
      }
    }
  }
  /**
   * 结束所有Activity
   */
  public void finishAllActivity() {
    for (int i = 0, size = activityStack.size(); i < size; i++) {
      if (null != activityStack.get(i)) {
       //finishActivity方法中的activity.isFinishing()方法会导致某些activity无法销毁
       //貌似跳转的时候最后一个activity 是finishing状态,所以没有执行
       //内部实现不是很清楚,但是实测结果如此,使用下面代码则没有问题
       // find by TopJohn
       //finishActivity(activityStack.get(i));
       activityStack.get(i).finish();
       //break;
      }
    }
    activityStack.clear();
  }
  /**
   * 获取指定的Activity
   *
   * @author kymjs
   */
  public static Activity getActivity(Class<?> cls) {
    if (activityStack != null)
      for (Activity activity : activityStack) {
        if (activity.getClass().equals(cls)) {
          return activity;
        }
      }
    return null;
  }
  /**
   * 退出应用程序
   */
  public void AppExit(Context context) {
    try {
      finishAllActivity();
      // 杀死该应用进程
      android.os.Process.killProcess(android.os.Process.myPid());
      System.exit(0);
    } catch (Exception e) {
    }
  }
}

在上面onResume方法中,(这里单独拿出来),逻辑是更新版本号,查看当前安装的apk的版本号,与shared_prefs目录下first_install.

 @Override
  protected void onResume() {
    super.onResume();
    int cacheVersion = PreferenceHelper.readInt(this, "first_install",
        "first_install", -1);
    int currentVersion = TDevice.getVersionCode();
    if (cacheVersion < currentVersion) {
      PreferenceHelper.write(this, "first_install", "first_install",
          currentVersion);
      cleanImageCache();
    }
  }

first_install.

<??>
<map>
<int name="first_install" value="48" />
</map>

TDevice文件下的getVersionCode()方法:

public static int getVersionCode() {
    int versionCode = 0;
    try {
      versionCode = BaseApplication
          .context()
          .getPackageManager()
          .getPackageInfo(BaseApplication.context().getPackageName(),
              0).versionCode;
    } catch (PackageManager.NameNotFoundException ex) {
      versionCode = 0;
    }
    return versionCode;
  }

删除图像缓存:

  private void cleanImageCache() {
    final File folder = FileUtils.getSaveFolder("OSChina/imagecache");
    KJAsyncTask.execute(new Runnable() {
      @Override
      public void run() {
        for (File file : folder.listFiles()) {
          file.delete();
        }
      }
    });
  }

getSaveFolder方法:

  /**
   * 获取文件夹对象
   * 
   * @return 返回SD卡下的指定文件夹对象,若文件夹不存在则创建
   */
  public static File getSaveFolder(String folderName) {
    File file = new File(getSDCardPath() + File.separator + folderName
        + File.separator);
    file.mkdirs();
    return file;
  }

AppStart中开启了一个服务LogUploadService用来上传应用程序的日志。

在服务LogUploadService被开启后,根据情况进行如下几种操作:

  1. 读取osc本地文件夹下的日志信息
  2. 如果日志信息为空,服务停止—— LogUploadService.this.stopSelf()
  3. 如果日志信息不位空,上传日志;

当某一个组件比如Activity,通过调用startService()方法来开启一个服务时,系统会调用onStartCommand()方法。一旦这个方法执行之后,服务就会被开启并在后台独立的运行。如果你实现了这个方法,你必须在任务完成后通过调用stopSelf()或者stopService()来停止该服务。(如果你仅仅只想提供绑定,你不需要实现这个方法)。

日志上传的操作,封装在了OSChinaApi中,并且通过report来区分是bug还是反馈意见。

  /**
   * BUG上报
   * 
   * @param data
   * @param handler
   */
  public static void uploadLog(String data, AsyncHttpResponseHandler handler) {
    uploadLog(data, "1", handler);
  }
  /**
   * 反馈意见
   * 
   * @param data
   * @param handler
   */
  public static void feedback(String data, AsyncHttpResponseHandler handler) {
    uploadLog(data, "2", handler);
  }
uploadLog方法:
  private static void uploadLog(String data, String report,
      AsyncHttpResponseHandler handler) {
    RequestParams params = new RequestParams();
    params.put("app", "1");
    params.put("report", report);
    params.put("msg", data);
    ApiHttpClient.post("action/api/user_report_to_admin", params, handler);
  }

ApiHttpClient的post方法:

  public static void post(String partUrl, RequestParams params,
              AsyncHttpResponseHandler handler) {
    client.post(getAbsoluteApiUrl(partUrl), params, handler);
    log(new StringBuilder("POST ").append(partUrl).append("&")
        .append(params).toString());
  }

getAbsoluteapiUrl方法:

 public static String getAbsoluteApiUrl(String partUrl) {
    String url = partUrl;
    if (!partUrl.startsWith("http:") && !partUrl.startsWith("https:")) {
      url = String.format(API_URL, partUrl);
    }
    Log.d("BASE_CLIENT", "request:" + url);
    return url;
  }

API_URL的值:

 private static String API_URL = "http://www.oschina.net/%s";