使用Intellij IDEA阅读android源码

一直使用Ubuntu+Intellig IDEA进行android开发,并且android源码已经花了两三个星期下载回来了,但是linux平台,没有好用的source insight,所以一直阅读都是需要看哪个了才去搜索那一个。原来发现,原来android提供了eclipse,idea等工具进行阅读的方法。

在android源码目录有一个目录development/tools/idegen,这个就是用来生成idea的project文件的。

那么就开始生成吧!

首先在源码根目录执行这个文件
bash
sh ./development/tools/idegen/idegen.sh

发现需要idegen.jar文件,我这里贡献给大家,把这个文件复制到out/host/linux-x86/framework/目录下,然后重新执行刚才的命令即可。文件下载地址

给我的提示是:

Read excludes: 25ms
Traversed tree: 592981ms

即,总共花费这么多时间,当然电脑快的话,可能也会更快一点。

在我的源码目录生成了android.imlandroid.ipr两个文件。

然后在idea中点击file>open,选择刚刚生成的android.ipr文件即可。

android中网络操作使用总结(http)

Android是作为智能手机的操作系统,我们开发的应用,大多数也都需要连接网络,通过网络发送数据、获取数据,因此作为一个应用开发者必须熟悉怎么进行网络访问与连接。通常android中进行网络连接一般是使用scoket或者http,http是最多的情况,这里,我来总结下,怎么进行http网络访问操作。

android是采用java语言进行开发的,android的包中包含java的URLConnection和apache 的httpclient,因此我们可以使用这两个工具进行网络连接和操作。同时,为了控制是否允许程序连接网络,我们开发应用时,需要在Manifest文件中配置申请网络连接的权限,代码如下。
xml
<uses-permission android:name="android.permission.INTERNET"/>

使用URLConnection连接网络

URLConnection为java.net包中提供的网络访问,支持http,https,ftp等,进行http连接时,使用HttpURLConnection即可,示例代码如下:

URL url = new URL("http://www.android.com/");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
    InputStream in = new BufferedInputStream(urlConnection.getInputStream());
    readStream(in);   //该方法是我们自己写的,从流中取数据保存到本地,实现从网络获取数据。
finally {
    urlConnection.disconnect();
}

向服务器提交数据时,需要采用POST传输
“`java
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();<br/>
try {<br/>
urlConnection.setDoOutput(true); //设置可以上传数据<br/>
urlConnection.setChunkedStreamingMode(0);</p>

<pre><code>OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
writeStream(out); //该方法时我们自己写的,向输出流中写数据,实现数据上传。

InputStream in = new BufferedInputStream(urlConnection.getInputStream());
readStream(in);
</code></pre>

<p>finally {<br/>
urlConnection.disconnect();<br/>
}<br/>
“`
更多使用方法和函数请查看:http://developer.android.com/reference/java/net/HttpURLConnection.html

同时HttpsURLConnection的使用方法请查看:http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html

使用HttpClient

这个呢,我们也可以使用AndroidHttpClient页可以使用apache默认的DeafultHttpClient,两者之间仅仅常见Client不同,其他都一样

创建AndroidHttpClient
java
AndroidHttpClient httpClient = AndroidHttpClient.newInstance("user-agent");
java
创建DefaultHttpClient
java
HttpClient client = new DefaultHttpClient();

GET请求
“`java
HttpGet getRequest = new HttpGet("<a href="http://blog.isming.me%22">http://blog.isming.me"</a>);<br/>
try {<br/><br/>
HttpResponse response = httpClient.execute(getMethod); //发起GET请求 </p>

<pre><code>Log.i(TAG, "resCode = " + response.getStatusLine().getStatusCode()); //获取响应码<br />
Log.i(TAG, "result = " + EntityUtils.toString(response.getEntity(), "utf-8"));//获取服务器响应内容<br />
</code></pre>

<p>} catch (ClientProtocolException e) {<br/><br/>
// TODO Auto-generated catch block<br/><br/>
e.printStackTrace();<br/><br/>
} catch (IOException e) {<br/><br/>
// TODO Auto-generated catch block<br/><br/>
e.printStackTrace();<br/><br/>
}<br/><br/>

<pre><code><br />发起POST请求:<br />
“`java<br />
//先将要传的数据放入List<br/><br/>
params = new LinkedList<BasicNameValuePair>();<br/><br/>
params.add(new BasicNameValuePair(“param1”, “Post方法”));<br/><br/>
params.add(new BasicNameValuePair(“param2”, “第二个参数”)); </p>

<p>try {<br/><br/>
HttpPost postMethod = new HttpPost(baseUrl);<br/><br/>
postMethod.setEntity(new UrlEncodedFormEntity(params, “utf-8”)); //将参数填入POST Entity中 </p>

<pre><code>HttpResponse response = httpClient.execute(postMethod); //执行POST方法
Log.i(TAG, “resCode = ” + response.getStatusLine().getStatusCode()); //获取响应码
Log.i(TAG, “result = ” + EntityUtils.toString(response.getEntity(), “utf-8”)); //获取响应内容
</code></pre>

<p>} catch (UnsupportedEncodingException e) {<br/><br/>
// TODO Auto-generated catch block<br/><br/>
e.printStackTrace();<br/><br/>
} catch (ClientProtocolException e) {<br/><br/>
// TODO Auto-generated catch block<br/><br/>
e.printStackTrace();<br/><br/>
} catch (IOException e) {<br/><br/>
// TODO Auto-generated catch block<br/><br/>
e.printStackTrace();<br/><br/>
}<br/><br/>

说明

从上面看到,使用HttpClient更加方便,明了。不过Android官方给我们的文档说明是建议android2.2及以下版本建议使用HttpClient,android2.3及以上版本建议使用HttpURLConnection。

原因是HttpURLConnection时一个轻量级的Http客户端,提供的API比较简单,方便扩展,但是在2.2及以前版本中,这个API存在一些bug,在2.3的版本中,给修改了。

而HttpClient虽然稳定,bug少,但是其提供的API很多,进行升级和扩展不方便,android团队在提升和优化其方面的积极性不高。

因此,在Android2.2版本以前,使用HttpClient时最好的选择。

Android2.3版本及以后,使用HttpURLConnection时最佳选择。

框架

网络连接是耗时的操作,通常我们不会将其放在UI线程操作,会编写异步线程,将其放在后台线程执行(异步线程操作看这里)。

而网上有一些线程的网络操作框架,可以减少我们的工作量,同时我们自己写的好,可能很多异常处理之类的,没有其向的更加全面。

有轻量的android-async-http

谷歌官方的volley

Square提供的Retrofit

这个可以根据自己项目的需要选择不同的框架,有空我来写写volley的使用(PS:网上已经有使用方法了,自己百度去),另外两个我的连接中已经有使用方法了。

ok,完成!

写的东西不少,但是介绍的不够详细。如果不清楚的,欢迎与我交流

ubuntu快速变装mac

其实,我喜欢Mac的,想要有个MacBook,喜欢其婀娜多姿的身材,妩媚的脸庞,最终要的是有一个UNIX的心。可惜,屌丝买不起啊,只好用Ubuntu来装装Mac了,有什么办法变装呢,那就是安装主题,哈哈。

之前的时候,还是Ubuntu13.04的时候,用过一个主题,让我的桌面变得真的很像Mac,但是升级到14.04之后,发现那个主题安装不了了。今天偶然发现,原来是作者对其升级了,针对不同版本安装不同的主题包,然后我又恢复原来的那个界面了。遂分享之。

先来晒晒我的界面,(__) 嘻嘻……

佛说,万物皆有源!首先,我们要先将该软件的源加到我们的源列表中。


sudo add-apt-repository ppa:noobslab/themes
sudo apt-get update

然后针对不同版本,安装不同的主题

Ubuntu13.04(更低版本,可以用这个试一下,不保证可以用)


sudo apt-get install mac-icons-noobslab
sudo apt-get install mac-ithemes-noobslab

Ubuntu13.10

sudo apt-get install mac-icons-v2-noobslab
sudo apt-get install mac-ithemes-v2-noobslab

Ubuntu14.04

sudo apt-get install mac-icons-v3
sudo apt-get install mac-ithemes-v3

执行完之后,需要安装Ubuntu Tweak ,在theme中进行更多的设置,我的配置如下图所示。

Android消息循环分析

我们的常用的系统中,程序的工作通常是有事件驱动和消息驱动两种方式,在Android系统中,Java应用程序是靠消息驱动来工作的。

消息驱动的原理就是:

1. 有一个消息队列,可以往这个队列中投递消息;

2. 有一个消息循环,不断从消息队列中取出消息,然后进行处理。

在Android中通过Looper来封装消息循环,同时在其中封装了一个消息队列MessageQueue。

另外Android给我们提供了一个封装类,来执行消息的投递,消息的处理,即Handler。

在我们的线程中实现消息循环时,需要创建Looper,如:

class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare(); //1.调用prepare
        ......
        Looper.loop();  //2.进入消息循环
    }
}

看上面的代码,其实就是先准备Looper,然后进入消息循环。

1. 在prepare的时候,创建一个Looper,同时在Looper的构造方法中创建一个消息队列MessageQueue,同时将Looper保存到TLV中(这个是关于ThreadLocal的,不太懂,以后研究了再说)

2. 调用loop进入消息循环,此处其实就是不断到MessageQueue中取消息Message,进行处理。

然后再看我们如何借助Handler来发消息到队列和处理消息

Handler的成员(非全部):


final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;

Message的成员(非全部):


Handler target;
Runnable callback;

可以看到Handler的成员包含Looper,通过查看源代码,我们可以发现这个Looper是有两种方式获得的,1是在构造函数传进来,2是使用当前线程的Looper(如果当前线程无Looper,则会报错。我们在Activity中创建Handler不需要传Handler是因为Activity本身已经有一个Looper了),MessageQueue也就是Looper中的消息队列。

然后我们看怎么向消息队列发送消息,Handler有很多方法发送队列(这个自己可以去查),比如我们看sendMessageDelayed(Message msg, long delayMillis)

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
// SystemClock.uptimeMillis() 获取开机到现在的时间
}
//最终所有的消息是通过这个发,uptimeMillis是绝对时间(从开机那一秒算起)
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
return sent;
}

看上面的的代码,可以看到Handler将自己设为Message的target,然后然后将msg放到队列中,并且指定执行时间。

消息处理

处理消息,即Looper从MessageQueue中取出队列后,调用msg.target的dispatchMessage方法进行处理,此时会按照消息处理的优先级来处理:

1. 若msg本身有callback,则交其处理;

2. 若Handler有全局callback,则交由其处理;

3. 以上两种都没有,则交给Handler子类实现的handleMessage处理,此时需要重载handleMessage。

我们通常采用第三种方式进行处理。

注意!!!!我们一般是采用多线程,当创建Handler时,LooperThread中可能还未完成Looper的创建,此时,Handler中无Looper,操作会报错。

我们可以采用Android为我们提供的HandlerThread来解决,该类已经创建了Looper,并且通过wait/notifyAll来避免错误的发生,减少我们重复造车的事情。我们创建该对象后,调用getLooper()即可获得Looper(Looper未创建时会等待)。

补充

本文所属为Android中java层的消息循环机制,其在Native层还有消息循环,有单独的Looper。并且2.3以后MessageQueue的核心向Native层下移,native层java层均可以使用。这个我没有过多的研究了!哈哈

PS:本文参考《深入理解Android:卷I》

管理Activity的生命周期

Activity是android的四大组件之一,我们编写程序时,主要通过Activity来显示我们的UI。我们需要了解他的生命周期,以及 每个周期可以做什么。
一个Activity存在三种状态:

Resumed:

activity显示在屏幕的最前面,并且获取用户焦点。

Paused:

其他activity在当前activity之前,并获得焦点。当前activity还能够部分显示,仍然维护着所有状态,当内存低的时候才会被系统杀死。

Stopped:

当前activity完全不可见。但是仍然存在,其他应用需要内存的时候会被杀死(不一定是低内存的时候)。

具体生命周期见图:

  1. 启动Activity
    执行onCreate() –> onStart() –> onResumed()
    这个过程执行完,我们的activity就显示在屏幕上了。
    我们可以重写相应的方法,在其中实现我们需要的操作。
  2. 销毁Activity
    当我们执行finish()或者被系统强制杀死时,我们的activity会被销毁。
    我们的activity内部会执行 onPause()–>onStop()->onDestory()
  3. 暂停和继续
    当我们的界面被部分挡住时,会进入暂停状态。
    此时会执行onPause()
    界面重新完全显示后又会回到继续状态(Resumed),会执行onResumed()
    在即将pause时,我应应该在onPause中执行一些释放操作,比如停止正在进行的动画,一些用户的状态(确定用户会保存的,比如邮件草稿),释放系统支援(如广播接收者,传感器),以及其他会消耗电池并且在paused时用户不需要用到的。
    同时这些释放或者保存的,我们在onResumed时候需要恢复。
  4. 停止和重启
    用户进去其他界面之后,如接听电话,或者打开其他activity,我们的界面会停止,进入stoped状态。此时会执行onPause()–>onStop()
    重启时会执行onRestart()–>onStart()–>onResume()
    重启时候和我们创建时相比,多一步onRestart()。
    ​在stop的时候,我们需要执行一些比在onPause中更加消耗CPU的更大的任务,比如写数据库。
    ​同时建议,在onStart()中恢复,而不是在onRestart()中恢复。
  5. 重新创建Activity
    ​ ​当我们的进程被destory(不是用户主动调用finish),可以返回。返回的时候会重新创建。执行过程和创建activity一样。
    ​当activity被系统kill之前,会调用onSaveInstanceState()去保存UI状态,如果我们有信息需要保存,也可以去重写这个方法去做。
    同时我们可以重写onRestoreInstanceState()去恢复状态。不重写,系统会恢复系统保存的那一部分UI。或者我们可以在onCreate中恢复,onCreate的参数savedInstanceState就是我们保存的信息,可以判断该参数是否为空,来恢复界面。

解决Ubuntu下Sublime text 3中文输入的问题

好久之前便听朋友说起Sublime Text这款软件很好用,终于这几天有空折腾,把软件给装起来了。用起来确实很不错,写代码很爽。

但是用了一段时间之后,我需要输入中文了,无论怎么切换输入法,都无法切换到中文。
网上搜索了一下,原来这是Bug。找解决方法吧。下面介绍我的解决方案,是大神cjacker解决成功的啦,我只是copy一下,方便大家在遇到这个问题的时候可以方便解决。

    我的系统:ubuntu 13.04  
    我的输入法:fcitx   
    sublime版本:3059    

理论上支持 sublime text2/3

1.保存代码sublime-imfix.c

/*
sublime-imfix.c
Use LD_PRELOAD to interpose some function to fix sublime input method support for linux.
By Cjacker Huang <jianzhong.huang at i-soft.com.cn>

gcc -shared -o libsublime-imfix.so sublime_imfix.c  `pkg-config --libs --cflags gtk+-2.0` -fPIC
LD_PRELOAD=./libsublime-imfix.so sublime_text
*/
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
typedef GdkSegment GdkRegionBox;

struct _GdkRegion
{
    long size;
    long numRects;
    GdkRegionBox *rects;
    GdkRegionBox extents;
};

GtkIMContext *local_context;

void
gdk_region_get_clipbox (const GdkRegion *region,
        GdkRectangle    *rectangle)
{   
    g_return_if_fail (region != NULL);
    g_return_if_fail (rectangle != NULL);

    rectangle->x = region->extents.x1;
    rectangle->y = region->extents.y1;
    rectangle->width = region->extents.x2 - region->extents.x1;
    rectangle->height = region->extents.y2 - region->extents.y1;
    GdkRectangle rect;
    rect.x = rectangle->x;
    rect.y = rectangle->y;
    rect.width = 0;
    rect.height = rectangle->height; 
    //The caret width is 2; 
    //Maybe sometimes we will make a mistake, but for most of the time, it should be the caret.
    if(rectangle->width == 2 && GTK_IS_IM_CONTEXT(local_context)) {
        gtk_im_context_set_cursor_location(local_context, rectangle);
    }
}

//this is needed, for example, if you input something in file dialog and return back the edit area
//context will lost, so here we set it again.

static GdkFilterReturn event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer im_context)
{
    XEvent *xev = (XEvent *)xevent;
    if(xev->type == KeyRelease && GTK_IS_IM_CONTEXT(im_context)) {
        GdkWindow * win = g_object_get_data(G_OBJECT(im_context),"window");
        if(GDK_IS_WINDOW(win))
            gtk_im_context_set_client_window(im_context, win);
    }
    return GDK_FILTER_CONTINUE;
}

void gtk_im_context_set_client_window (GtkIMContext *context,
      GdkWindow    *window)
{
    GtkIMContextClass *klass;
    g_return_if_fail (GTK_IS_IM_CONTEXT (context));
    klass = GTK_IM_CONTEXT_GET_CLASS (context);
    if (klass->set_client_window)
    klass->set_client_window (context, window);

    if(!GDK_IS_WINDOW (window))
    return;
    g_object_set_data(G_OBJECT(context),"window",window);
    int width = gdk_window_get_width(window);
    int height = gdk_window_get_height(window);
    if(width != 0 && height !=0) {
        gtk_im_context_focus_in(context);
        local_context = context;
    }
    gdk_window_add_filter (window, event_filter, context); 
}

2.安装C/C++的编译环境和gtk libgtk2.0-dev

sudo    apt-get install build-essential
sudo apt-get install libgtk2.0-dev

3.编译共享内存

gcc -shared -o libsublime-imfix.so sublime_imfix.c  `pkg-config --libs --cflags gtk+-2.0` -fPIC

4.启动测试

LD_PRELOAD = ./libsublime-imfix.so sublime_text

正常的话这样是没有问题的。

然后我们在修改我们的desktop文件,使图标也可以使用

sudo vi /usr/share/applications/sublime-text.desktop

先将so文件移动到sublime text的目录

然后按照如下替换(主要是每次执行之前,去预加载我们的libsublime-imfix.so库)

[Desktop Entry]
Version=1.0
Type=Application
Name=Sublime Text
GenericName=Text Editor
Comment=Sophisticated text editor for code, markup and prose
Exec=bash -c 'LD_PRELOAD=/opt/sublime_text/libsublime-imfix.so /opt/sublime_text/sublime_text' %F
Terminal=false
MimeType=text/plain;
Icon=sublime-text
Categories=TextEditor;Development;
StartupNotify=true
Actions=Window;Document;

[Desktop Action Window]
Name=New Window
Exec=bash -c 'LD_PRELOAD=/opt/sublime_text/libsublime-imfix.so /opt/sublime_text/sublime_text' -n
OnlyShowIn=Unity;

[Desktop Action Document]
Name=New File
Exec=bash -c 'LD_PRELOAD=/opt/sublime_text/libsublime-imfix.so /opt/sublime_text/sublime_text' --command new_file
OnlyShowIn=Unity;

Toast的使用详解

Android中提供一种简单的Toast消息提示框机制,可以在用户点击了某些按钮后,提示用户一些信息,提示的信息不能被用户点击,Toast的提示信息根据用户设置的显示时间后自动消失。Toast的提示信息可以在调试程序的时候方便的显示某些想显示的东西,或者给用户提供友好的界面显示效果。

有两种方式去创建并且显示Toast:

  1. Toast.makeText(Context context, int resId, int duration)

    Toast.makeText(Context context, CharSequence text, int duration)

    Context为上下文,通常为当前activity;resId是string字符串的id,CharSequence为你要显示的字符串,duration为显示的时间,可以选择Toast.LENGTH_SHORT或Toast.LENGTH_LONG,也可自定义时间。
    使用方法:

    Toast.makeText(this, "this is string", Toast.LENGTH_SHORT).show();

  2. 自己创建Toast,并且设置视图,即自定义

    如:

Toast toast = new Toast(this);      
// 定义一个ImageView        
ImageView imageView = new ImageView(this);      
imageView.setImageResource(R.drawable.ic_launcher);     
// 定义一个Layout,这里是Layout     
LinearLayout Layout = new LinearLayout(this);       
Layout.setOrientation(LinearLayout.HORIZONTAL);     
// 将ImageView放到Layout中      
Layout.addView(imageView);      
// 设置View       
toast.setView(Layout);      
//设置显示时间        
toast.setDuration(20);      
toast.show();       

通过上面的代码就可以自己定义一个Toast了,我们还在其中显示了图片。

如何设置Toast显示的位置

方法一:

setGravity(int gravity, int xOffset, int yOffset) 三个参数分别表示(起点位置,水平向右位移,垂直向下位移)

方法二:

setMargin(float horizontalMargin, float verticalMargin)
以横向和纵向的百分比设置显示位置,参数均为float类型(水平位移正右负左,竖直位移正上负下)

注意事项:

Toast中有一个public方法setText(),可以给toast设置resid或者string,该方式尽可以在我们的第一种方法中使用,第二种自定义toast的方式是不可以使用的,使用的话会抛出异常。

原因是使用第一种方式创建,Toast会自己创建一个view,即textview,而我们使用这个setText实际是向这个TextView设置内容,而自定义的View不会有这个控件,因此会报错。

android异步操作总结

Android中经常会有一些操作比如网络请求,文件读写,数据库操作,比较耗时,我们需要将其放在非UI线程去处理,此时,我们需要处理任务前后UI的变化和交互。我们需要通过类似js中异步请求处理,这里总结我所了解到的,方便自己记忆,也方便别人的浏览。

  1. AsyncTask

new AysncTask().execute();

AsyncTask会按照流程执行在UI线程和一个耗时的任务线程。

1.onPreExecute() 执行预处理,它运行于UI线程,可以为后台任务做一些准备工作,比如绘制一个进度条控件。

2.doInBackground(Params…) 后台进程执行的具体计算在这里实现,doInBackground(Params…)是AsyncTask的关键,此方法必须重载。在这个方法内可以使用publishProgress(Progress…)改变当前的进度值。

3.onProgressUpdate(Progress…) 运行于UI线程。如果在doInBackground(Params…) 中使用了publishProgress(Progress…),就会触发这个方法。在这里可以对进度条控件根据进度值做出具体的响应。

4.onPostExecute(Result) 运行于UI线程,可以对后台任务的结果做出处理,结果就是doInBackground(Params…)的返回值。此方法也要经常重载,如果Result为null表明后台任务没有完成(被取消或者出现异常)。

  1. Handler
    创建Handler时需要传Lopper,默认是UI线程的。
    通过Handler发送消息(Message)到主线程或者Handler的线程,

  2. Activity.runOnUiThread(Runnable)
    Runnable即可在UI线程执行

  3. View.post(Runnable)
    Runnable运行在UI线程
    View.post(Runnable)方法。在post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的来更新UI。

所有的异步操作原理本质都是通过Handler

基本上就这几种方法,当然也可自己使用消息循环常见类似的任务处理机制。

git使用技巧

这几天开始工作了,工作中使用了git进行项目管理,这才发现原来我以前所知道的git使用那只是一点皮毛。

写一些这几天用到的一些git技巧喽,以后有的话继续更新啦。

git status 查看当前的状态,那些文件修改了,那些文件创建还没有add的。

git add . 添加所有的修改

或者

git add 文件名或者文件路径,添加指定的

git stash 将没有commit的部分文件放到暂存栈去,这样从服务器pull文件的时候不会有问题。

git stash pop 是将暂存栈的东西拿回来
git stash clear 将暂存栈中的东西清空,要慎用,这样你放在暂存中的修改都将丢失

git reset 将所有git add 的撤销

将其他的分支中的某个修改合并到当前分支

  1. git cherry-pick sha1(用gitk 在那个分支上面可以看到)
  2. git reset –soft 合并当前分支和cherry过来的分支。两个分支的代码都保留 如果参数用 hard 那么本地的将会被抹掉
  3. git commit –ammend
    这样就完成了

修改两次前的提交

  1. git rebase -i HEAD~2
  2. 修改那个提交的状态,即把pick改为edit
  3. 回去修改文件
  4. git commit –amend
  5. git rebase –continue
    这样也就ok了

git里面很多东西很有用,好吧,以后好好学习。

Android图像开源视图:SmartImageView

项目需要,开发中需要加载图片,自己要写图片从网上下载的方法,还要写缓存,等等。

在网上找到一个开源项目,smartImageVIew,支持从URL和通讯录中获取图像,可以替代Android标准的ImageView。

特征:

根据URL地址装载图像;
支持装载通讯录中的图像;
支持异步装载;
支持缓存;

这个是作者的项目主页,有使用方法。

http://loopj.com/android-smart-image-view/

下载作者的jar包导入项目后,在xml中加入控件

<com.loopj.android.image.SmartImageView android:id="@+id/my_image" />    

代码里找到该控件

SmartImageView myImage = (SmartImageView) this.findViewById(R.id.my_image); 

使用控件

通过url加载图片

myImage.setImageUrl("http://www.awesomeimages.com/myawesomeimage.jpg");   

加载通讯录的图片

myImage.setImageContact(contactAddressBookId);  

github上面有源码,需要的可以看看:

https://github.com/loopj/android-smart-image-view