现在的位置: 首页 > 综合 > 正文

HTTP with HttpURLConnection_(MyMoviesWithUpdateNotice)

2017年12月24日 ⁄ 综合 ⁄ 共 11814字 ⁄ 字号 评论关闭

You need to perform simple networking tasks via HTTP, such as downloading a file, and you want to avoid the performance penalty imposed by the more high-level, much larger and more complex Apache HttpClient implementation.

URL url = new URL("http://www.example.com/");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
...
conn.disconnect();

HttpURLConnection inherits from the more generic URLConnection,how does URL know what kind of connection to return? The simple answer is that it depends on the URL’s scheme (such as http). A protocol handler class looks at the scheme and tries
to find a matching connection implementation.

URLConnection uses TCP sockets and the standard java.io stream classes. That means I/O is blocking, so remember to never run them on the main UI thread.

For this to work, we have to place a text file containing the update notes somewhere on a web server, download and read the file, and display its text in a message dialog.

The plan is to write an AsyncTask that establishes a connection to an HTTP server via HttpURLConnection and download the file containing the update notes text. We then send this text via a Handler object to our main activity so we can show an AlertDialog
with that text. Let’s first look at the MyMovies activity class, which contains the callback for the handler to show the pop-up dialog.

Androidmanifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="example.mymovieswithupdatenotice"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
	<supports-screens
	    android:smallScreens="true"
	    android:normalScreens="true"
	    android:largeScreens="true"
	    android:anyDensity="true" />
		
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/MyMoviesTheme" >
        <activity
            android:name=".SplashScreen"
            android:label="@string/title_activity_main" 
            android:theme="@style/SplashScreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MyMovies" />
    </application>
	<uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>

values/movies.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string-array name="movies">
    <item>The Shawshank Redemption</item>
    ......
 
    <item>The Apartment</item>
    <item>Gladiator</item>
    <item>The Sting</item>
    <item>Slumdog Millionaire</item>
  </string-array>
</resources>

values/movie_thumbs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string-array name="movie_thumbs">
    <item>http://ia.media-imdb.com/images/M/MV5BMTg4MDA2MDM5Nl5BMl5BanBnXkFtZTcwOTU5MTQ2Mg@@._V1._CR328,0,1392,1392_SS90_.jpg
    </item>
   ...........
    <item>http://ia.media-imdb.com/images/M/MV5BMTk4MDk2NDI5Nl5BMl5BanBnXkFtZTcwMDM3MjcwMg@@._V1._CR120,0,481,481_SS90_.jpg
    </item>
  </string-array>
</resources>

values/strings.xml

<resources>
    <string name="app_name">MyMoviesWithUpdateNotice</string>
    <string name="hello_world">Hello world!</string>
    <string name="menu_settings">Settings</string>
    <string name="title_activity_main">MainActivity</string>
</resources>

values/colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <color name="list_background">#A000</color>
</resources>

values/styles.xml

<resources>
	<style name="MyMoviesTheme" parent="@android:style/Theme.Black">
    <item name="android:listViewStyle">@style/MyMoviesListView</item>
    <item name="android:windowBackground">@drawable/film_bg</item>
  </style>
   <style name="MyMoviesListView" parent="@android:Widget.ListView">
    <item name="android:background">@color/list_background</item>
    <item name="android:listSelector">@drawable/list_selector</item>
    <item name="android:cacheColorHint">@android:color/transparent</item>
    <item name="android:fastScrollEnabled">true</item>
    <item name="android:footerDividersEnabled">false</item>
  </style>
  <style name="SplashScreen" parent="@android:style/Theme.Black">
    <item name="android:windowNoTitle">true</item>
  </style>
</resources>

layout/splash_screen.xml

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
  <ImageView 
      android:layout_width="fill_parent"
      android:layout_height="fill_parent" 
      android:scaleType="fitXY"
      android:src="@drawable/splash" />
</merge>

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    
    <ImageView android:src="@drawable/title"
      android:layout_width="fill_parent"
      android:layout_height="75px"
      android:scaleType="fitXY" />
    
    <ListView android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />
    
</LinearLayout>

movie_item.xml

<?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="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical"
    >

    <ImageView 
       android:id="@+id/movie_icon"
       android:layout_width="50dip"     
       android:layout_height="50dip"
       android:scaleType="centerCrop"     
     />

    <CheckedTextView android:id="@android:id/text1"
        android:layout_width="0px"
        android:layout_height="fill_parent"
        android:layout_weight="0.9"
        android:gravity="center_vertical"
        android:paddingLeft="6dip"
        android:paddingRight="6dip"
        android:checkMark="?android:attr/listChoiceIndicatorMultiple"
    />     
</LinearLayout>

list_footer.xml

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical"
    android:text="Back to top"
    android:onClick="backToTop"
    />

SplashScreen.class

public class SplashScreen extends Activity {
	public static final int SPLASH_TIMEOUT=2000;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.splash_screen);
        new Timer().schedule(new TimerTask(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				proceed();
			}
        }, SPLASH_TIMEOUT);
    }

   private void proceed(){
	   if(this.isFinishing())
		   return;
	   startActivity(new Intent(SplashScreen.this,MyMovies.class));
   }
   
   public boolean onTouchEvent(MotionEvent event){
	   if(event.getAction()==MotionEvent.ACTION_DOWN)
		   proceed();
	   return super.onTouchEvent(event);
   }
}

MovieAdapter.class

public class MovieAdapter extends ArrayAdapter<String> {
	private HashMap<Integer,Boolean> movieCollection=new HashMap<Integer,Boolean>();
	private String[] moiveIconUrls;
	
	public MovieAdapter(Context context) {
		super(context,R.layout.movie_item,android.R.id.text1,context.getResources().getStringArray(R.array.movies));
		// TODO Auto-generated constructor stub
		moiveIconUrls=context.getResources().getStringArray(R.array.movie_thumbs);
	}
	
	public  void toggleMovie(int position){
		if(!isInCollection(position))
			movieCollection.put(position, true);
		movieCollection.put(position, false);
			
	}
	public boolean isInCollection(int position){
		return movieCollection.get(position)==Boolean.TRUE;
	}
	
	public View getView(int position,View convertView,ViewGroup parent){
		View listItem=super.getView(position, convertView, parent);
		
		CheckedTextView checkMark=(CheckedTextView)listItem.findViewById(android.R.id.text1);
		checkMark.setChecked(isInCollection(position));
		
		ImageView imageView=(ImageView)listItem.findViewById(R.id.movie_icon);
		imageView.setImageDrawable(null);
		imageView.setTag(position); MyMovies
		String imageUrl=this.moiveIconUrls[position];
		new DownloadTask(position,imageView).execute(imageUrl);
		return listItem;
		
	}

}

DownloadTask.class

public class DownloadTask extends AsyncTask<String, Void, Bitmap> {

	private int position;
	private ImageView imageView;
	private Drawable placeHolder;
	
	public DownloadTask(int position,ImageView imageView){
		this.position=position;
		this.imageView=imageView;
		Resources resources=imageView.getContext().getResources();
		placeHolder=resources.getDrawable(android.R.drawable.gallery_thumb);
	}
	
	protected void onPreExecute(){
		imageView.setImageDrawable(placeHolder);
	}
	@Override
	protected Bitmap doInBackground(String... inputUrls) {
		// TODO Auto-generated method stub
		try{
			URL url=new URL(inputUrls[0]);
			return BitmapFactory.decodeStream(url.openStream());
			
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}
	}
	protected void onPostExecute(Bitmap result){
		int forPosition=(Integer)imageView.getTag();
		if(forPosition==this.position)
			this.imageView.setImageBitmap(result);
	}

}

MyMovies.class

public class MyMovies extends ListActivity implements Callback {
	private MovieAdapter adapter;
	public void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		ListView listView=getListView();
		
		Button backToTop=(Button)getLayoutInflater().inflate(R.layout.list_footer, null);
		backToTop.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(android.R.drawable.ic_menu_upload),
				null, null, null);
		listView.addFooterView(backToTop);
		
		this.adapter=new MovieAdapter(this);
		listView.setAdapter(adapter);
		listView.setItemsCanFocus(false);
		
		new UpdateNoticeTask(new Handler(this)).execute();//starts new download task
	}
	
    public void backToTop(View view) {
        getListView().setSelection(0);
    }
    
    protected void onListItemClick(ListView l, View v, int position, long id) {
    	this.adapter.toggleMovie(position);
    	this.adapter.notifyDataSetInvalidated();
    }
    
	@Override
	public boolean handleMessage(Message msg) {
		// TODO Auto-generated method stub
		String updateNotice=msg.getData().getString("text");//reads update text
		AlertDialog.Builder dialog=new AlertDialog.Builder(this);
		dialog.setTitle("What's new");
		dialog.setMessage(updateNotice);//set update text
		dialog.setIcon(android.R.drawable.ic_dialog_info);
		dialog.setPositiveButton(getString(android.R.string.ok), new OnClickListener(){
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// TODO Auto-generated method stub
				dialog.dismiss();
			}
		});
		dialog.show();
		return false;
	}

}

UpdateNoticeTask.class

public class UpdateNoticeTask extends AsyncTask<Void,Void,String> {
	private static final String UPDATE_URL =
        "http://android-in-practice.googlecode.com/files/update_notice.txt";
	private HttpURLConnection connection;
	private Handler handler;
	public UpdateNoticeTask(Handler handler){
		this.handler=handler;
	}
	
	@Override
	protected String doInBackground(Void... params) {
		// TODO Auto-generated method stub
		try{
			URL url=new URL(UPDATE_URL);
			//get instance of HttpURLConnection
			connection=(HttpURLConnection)url.openConnection();
			//configure request
			connection.setRequestMethod("GET");
			connection.setRequestProperty("Accept", "text/plain");
			connection.setReadTimeout(10);
			connection.setConnectTimeout(10);
			//establish connection
			connection.connect();
			int statusCode=connection.getResponseCode();
			if(statusCode!=HttpURLConnection.HTTP_OK)
				//handl non-200 reply
				return "error: Failed getting update notes";
			return readTextFromServer();//reand text from response
		}catch(Exception e){
			return "Error: " + e.getMessage();
		}finally{
			if(connection!=null)
				//close connection
				connection.disconnect();
		}
	}
	
	private String readTextFromServer() throws IOException{
		InputStreamReader isr=new InputStreamReader(connection.getInputStream());
		BufferedReader br=new BufferedReader(isr);
		StringBuilder sb=new StringBuilder();
		String line=br.readLine();
		while(line!=null){
			sb.append(line+"\n");
			line=br.readLine();
		}
		return sb.toString();
		
	}
	//pass retrive text to activity
	protected void onPostExecute(String updateNotice){
		Message message=new Message();
		Bundle data=new Bundle();
		data.putString("text", updateNotice);
		message.setData(data);
		handler.sendMessage(message);
	}
	
}

The request, response, and the mechanisms to send and receive them are merged into a single class, often leaving you wondering which methods to use to process which part of this triplet.

It’s not a beaming example of good object-oriented class design.

HttpURLConnection in Apache Harmony has bugs—serious bugs. To summarize, HttpURLConnection is a simply structured, but low-level way of doing

HTTP messaging. A few negative aspects about it stand out:

Its clunky interface makes it difficult to use.

Its monolithic design and lack of object-orientation impede testability and configuration/ customization

It suffers from bugs that can turn out to be show stoppers.

For simple tasks such as the file download shown here, it’s fine and comes with the  least overhead (it doesn’t take a sledgehammer to crack a nut). But if you want to do
more complex things such as request interception, connection pooling, or multipart file uploads, then don’t bother with it.

抱歉!评论已关闭.