How to turn a piece of bad code into elegant code
Original article, if reproduced, please indicate the source: http://blog.csdn.net/yihui823/article/details/6741411
Today I saw a bad piece of code. So I made a project, simulated this code, and explained to you the points that should be paid attention to when using the thread. This example is suitable for beginners. You are also welcome to give some advice.
First, go to the code.
The first type is loginservice, which is a simulation class that disconnects the business. It is just a simulated login operation.
Package COM. study; Package COM. study;/*** a virtual Logon Service. * @ author yihui823 */public class loginservice {// Private Static loginservice oneinstance = new loginservice (); /*** get a unique Singleton * @ return unique Singleton */public static loginservice getinstance () {return oneinstance ;} // mark "Private Boolean hadlogin = false" for Successful Logon./*** simulate logon operation * @ return true: logon successful */Public Boolean login () {try {thread. sleep (2000);} catch (interruptedexception e) {} hadlogin = true; return hadlogin;}/*** determine whether to log on * @ return true: logged On */Public Boolean islogin () {return hadlogin ;}}
The second class is our activity.
Package COM. study; import android. app. activity; import android. OS. bundle; import android. widget. toast;/*** a bad code segment * @ author yihui823 */public class badcodeactivity extends activity {// log on to the private loginservice lservice = loginservice. getinstance ();/** called when the activity is first created. * // @ override public void oncreate (bundle savedinstancestate) {super. oncreate (savedinstancestate); setcont Entview (R. layout. Main); New thread (New runnable () {@ overridepublic void run () {While (! Lservice. islogin () {try {lservice. login (); thread. sleep (1000);} catch (interruptedexception e) {e. printstacktrace () ;}} toast. maketext (badcodeactivity. this, "Logon successful", toast. length_long ). show ();}}). start ();}}
In this example, it is clear that an error will be reported during running. See my other article: thread constraints in the android UI. To control the UI interface in a non-UI thread, handler must be used to send messages. The modification code is as follows:
Package COM. study; import android. app. activity; import android. OS. bundle; import android. OS. handler; import android. OS. message; import android. widget. toast;/*** a bad code segment * @ author yihui823 */public class badcodeactivity extends activity {// log on to the private loginservice lservice = loginservice. getinstance (); // The handleprivate handler mhandle = new handler () {@ overridepublic void handlemessage (message m SG) {toast. maketext (badcodeactivity. this, "Logon successful", toast. length_long ). show () ;};/** called when the activity is first created. * // @ override public void oncreate (bundle savedinstancestate) {super. oncreate (savedinstancestate); setcontentview (R. layout. main); New thread (New runnable () {@ overridepublic void run () {While (! Lservice. islogin () {try {lservice. login (); thread. sleep (1000);} catch (interruptedexception e) {e. printstacktrace () ;}} mhandle. sendemptymessage (0 );}}). start ();}}
The red part of the code is the modification. Now, this code can run, and it looks good, right.
However, a good program cannot only cope with normal situations, but also handle errors, right. What if an error occurs during login? We slightly changed the loginservice class as follows:
Package COM. study; import android. util. log;/*** a virtual Logon Service. * @ author yihui823 */public class loginservice {Private Static final string tag = "loginservice"; // Single Instance Private Static loginservice oneinstance = new loginservice (); /*** get a unique Singleton * @ return unique Singleton */public static loginservice getinstance () {return oneinstance ;} // mark "Private Boolean hadlogin = false" for Successful Logon./*** simulate logon operation * @ return true: logon successful */Public Boolean login () {try {thread. sleep (2000);} catch (interruptedexception e) {} log. D (TAG, "We are login"); // hadlogin = true; return hadlogin;}/*** determine whether to log on * @ return true: logged On */Public Boolean islogin () {return hadlogin ;}}
Added logs to view logon information. Only one line of the simulated Business Code is changed, that is, login will always fail. Run it now. There is no movement on the page, and logcat keeps playing:
We are Login
There will be no errors, right. However, if we press the "return" key to exit the page and then look at logcat?
We are constantly outputting login logs, right?
I think now everyone should know what went wrong. That is to say, after the thread is started, it cannot be stopped.
Here I want to talk about it. I always think that,
New thread (New runnable (){...} (). Start ();
This type of code is very poorly written. You directly construct an object, but you do not have any variables to point to it. After this thread is started, you can no longer trace, call, and manage it. This thread can only be self-generated and permanently exclusive to your control. Do you think this thread is the same as a zombie? Yes, this is a zombie process. If it has no conditions to stop, it will always consume your resources in your system.
So I think a basic understanding of using a thread: The generated Thread class must have a variable pointing to it so that it can be destroyed when appropriate.
Destruction is another problem. The stop method has been discarded for the Thread class, because the thread needs to release the released resources on its own and cannot rely solely on the control of the runtime framework. We need to add the code that stops by ourselves in the thread. That is to say, in any case, the thread should stop itself, instead of running without limit.
In addition, we should also pay attention to the turnover of activities in the Android system. Generally, it is inappropriate to start a thread in oncreate. We must consider onresume and onpause.
In summary, there are three notes for using threads in an activity:
1. The thread object must have a variable pointing to it so that we can control it.
2. The Thread class must have a stop condition so that the outside world notifies the thread to stop itself.
3. Start the thread in onresume and stop the thread in onpause..
Rewrite the activity based on the preceding three points.
Package COM. study; import android. app. activity; import android. OS. bundle; import android. OS. handler; import android. OS. message; import android. util. log; import android. widget. toast;/*** a bad code segment * @ author yihui823 */public class badcodeactivity extends activity {Private Static final string tag = "badcodeactivity "; // log on to the private loginservice lservice = loginservice. getinstance (); // The External Thread accesses the handleprivate of the UI thread. Handler mhandle = new handler () {@ overridepublic void handlemessage (Message MSG) {toast. maketext (badcodeactivity. this, "Logon successful", toast. length_long ). show () ;}}; // indicates the private Boolean stopflag = false for the notification stop thread; // indicates the private Boolean loginok = false for successful logon; /*** login Thread class */private class loginthread extends thread {@ overridepublic void run () {While (! Stopflag) {loginok = lservice. islogin (); If (loginok) {break;} Try {lservice. login (); thread. sleep (1000);} catch (interruptedexception e) {e. printstacktrace () ;}} mhandle. sendemptymessage (0);}/*** the notification thread needs to stop */Public void stoplogin () {stopflag = true ;}}; // The login thread private loginthread = new loginthread ();/** called when the activity is first created. * // @ override public void oncreate (bundle savedinstancestate) {super. oncreate (savedinstancestate); setcontentview (R. layout. main); log. D (TAG, "badcodeactivity instance is called oncreate:" + this. hashcode ();} public void onresume () {super. onresume (); log. D (TAG, "badcodeactivity instance is called onresume:" + this. hashcode (); loginthread. start ();} public void onpause () {super. onpause (); loginthread. stoplogin ();}}
Now, our thread can stop normally when the page exits.
However, this code is still problematic. Let's take a closer look. The thread has been created when the activity is constructed, started when the program enters the foreground, and stopped when it is returned to the background. However, threads have the following features:
Once the run () function of the thread ends, the thread is destroyed and cannot be started again.
Now our program will not be displayed again after exiting, so the system will immediately recycle the activity. If we add a button to the page and migrate it to another page, an exception will occur when the page returns. Let's modify the code to try it.
Add an activity:
Package COM. study; import android. app. activity; import android. OS. bundle;/*** temporary page * @ author yihui823 */public class tempactivity extends activity {/** called when the activity is first created. * // @ override public void oncreate (bundle savedinstancestate) {super. oncreate (savedinstancestate); setcontentview (R. layout. main );}}
Don't forget to modify androidmanifest. XML to add the activity description:
<Activity Android: Name = ". tempactivity"
Android: Label = "@ string/app_name"/>
Modify badcodeactivity:
Package COM. study; import android. app. activity; import android. content. intent; import android. OS. bundle; import android. OS. handler; import android. OS. message; import android. util. log; import android. view. view; import android. view. view. onclicklistener; import android. widget. button; import android. widget. toast;/*** a bad code segment * @ author yihui823 */public class badcodeactivity extends activity {Private Static fin Al string tag = "badcodeactivity"; // log on to the private loginservice lservice = loginservice. getinstance (); // The handleprivate handler mhandle = new handler () {@ overridepublic void handlemessage (Message MSG) {toast. maketext (badcodeactivity. this, "Logon successful", toast. length_long ). show () ;}}; // indicates the private Boolean stopflag = false for the notification stop thread; // indicates the private Boolean loginok = false for successful logon; /*** login Thread class */private class logint Hread extends thread {@ overridepublic void run () {While (! Stopflag) {loginok = lservice. islogin (); If (loginok) {break;} Try {lservice. login (); thread. sleep (1000);} catch (interruptedexception e) {e. printstacktrace () ;}} mhandle. sendemptymessage (0);}/*** the notification thread needs to stop */Public void stoplogin () {stopflag = true ;}}; // The login thread private loginthread = new loginthread ();/** called when the activity is first created. * // @ override public void oncreate (bundle savedinstancestate) {super. oncreate (savedinstancestate); setcontentview (R. layout. main); button BTN = (button) findviewbyid (R. id. BTN); BTN. setonclicklistener (New onclicklistener () {@ overridepublic void onclick (view arg0) {startactivity (new intent (badcodeactivity. this, tempactivity. class) ;}}); log. D (TAG, "badcodeactivity instance is called oncreate:" + this. hashcode ();} public void onresume () {super. onresume (); log. D (TAG, "badcodeactivity instance is called onresume:" + this. hashcode (); loginthread. start ();} public void onpause () {super. onpause (); loginthread. stoplogin ();}}
In fact, a button is added for page migration. Don't forget to add the following in Main. xml:
<Buttonandroid:id="@+id/btn" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" />
Now we run the program. After running, click the button. If the screen is flashing, the page is switched. We stole a layout, and the two activities shared the layout, so the page did not change. But it doesn't matter. We can see that log, We are login has stopped output. At this time, we press back to switch back to badcodeactivity. At this time, the system reports an error:
Java. Lang. illegalthreadstateexception: thread already started.
Obviously, the thread has been started and cannot be reused.
We need to make some modifications to the code. Of course, we can also get rid of a bug: We will also Report Logon success when exiting.
In addition, we place control variables in internal classes to minimize the living space of variables.
The modification is as follows:
Package COM. study; import android. app. activity; import android. content. intent; import android. OS. bundle; import android. OS. handler; import android. OS. message; import android. util. log; import android. view. view; import android. view. view. onclicklistener; import android. widget. button; import android. widget. toast;/*** a bad code segment * @ author yihui823 */public class badcodeactivity extends activity {Private Static fin Al string tag = "badcodeactivity"; // log on to the private loginservice lservice = loginservice. getinstance (); // The handleprivate handler mhandle = new handler () {@ overridepublic void handlemessage (Message MSG) {toast. maketext (badcodeactivity. this, "Logon successful", toast. length_long ). show () ;}};/*** login Thread class */private class loginthread extends thread {// mark the notification stop thread as private Boolean stopflag = false; // Private Bo is successfully marked upon logon. Olean loginok = false; @ overridepublic void run () {While (! Stopflag) {loginok = lservice. islogin (); If (loginok) {break;} Try {lservice. login (); thread. sleep (1000);} catch (interruptedexception e) {e. printstacktrace () ;}} if (loginok) {mhandle. sendemptymessage (0) ;}}/*** the notification thread needs to stop */Public void stoplogin () {stopflag = true ;}}; // The Private loginthread used to log on; /** called when the activity is first created. * // @ override public void oncreate (bundle savedinstancestate) {super. oncreate (savedinstancestate); setcontentview (R. layout. main); button BTN = (button) findviewbyid (R. id. BTN); BTN. setonclicklistener (New onclicklistener () {@ overridepublic void onclick (view arg0) {startactivity (new intent (badcodeactivity. this, tempactivity. class) ;}}); log. D (TAG, "badcodeactivity instance is called oncreate:" + this. hashcode ();} public void onresume () {super. onresume (); log. D (TAG, "badcodeactivity instance is called onresume:" + this. hashcode (); loginthread = new loginthread (); loginthread. start ();} public void onpause () {super. onpause (); loginthread. stoplogin ();}}
Now, click the button to stop log output when tempactivity is reached. Then, press the return key to continue log output when badcodeactivity is returned. The program is basically completed and no zombie thread exists. The line of red code is the key!
Let's summarize:
1. The thread object must have a variable pointing to it so that we can control it.
2. The Thread class must have a stop condition so that the outside world notifies the thread to stop itself.
3. After a thread is started, it cannot be reused whether it has been stopped or not.
4,Create and start threads in onresume and stop threads in onpause.