Showing posts with label Android remote service tutorial. Show all posts
Showing posts with label Android remote service tutorial. Show all posts

Friday, May 9, 2014

Android AIDL Remote Services and Upload File from Android Device to Server

Android AIDL Remote Services and Upload File from Android Device to Server
What is Service                    
                    A Service is an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another application. Additionally, a component can bind to a service to interact with it and even perform interprocess communication (IPC). For example, a service might handle network transactions, play music, perform file I/O, or interact with a content provider, all from the background.
Types of service
                There are two types of Service 1.Local service 2.Remote service. The local service component can be called only the components of same application,but Remote services can be called and may be shared by other application components.To implement remote service AIDL file which is similar to java interface with file extension .aidl.To bind service running in other process and perform inter process communication (IPC) this is required.
AIDL


AIDL (Android Interface Definition Language) is similar to other IDLs you might have worked with. It allows you to define the programming interface that both the client and service agree upon in order to communicate with each other using interprocess communication (IPC). On Android, one process cannot normally access the memory of another process. So to talk, they need to decompose their objects into primitives that the operating system can understand, and marshall the objects across that boundary for you. The code to do that marshalling is tedious to write, so Android handles it for you with AIDL.
Bound Service
                           Bound service is "bound" when an application component binds to it by calling bindService(). A bound service offers a client-server interface that allows components to interact with the service, send requests, get results, and even do so across processes with interprocess communication (IPC). A bound service runs only as long as another application component is bound to it. Multiple components can bind to the service at once, but when all of them unbind, the service is destroyed.
                                                                  The demonstrative project has two applications,one is Remote service application SRM_UploadFileRemoteService which  reads a file from SD card,with file name provided by client application SRM_FileUploadClient and uploads it to  php webserver.

      Use URL http://navinsandroidtutorial.comlu.com/upload/upload.php instead of localhost  to upload file from android device.You can  view the uploaded file by typing the url in the browser http://navinsandroidtutorial.comlu.com/upload/uploads/file-name

1.Create  Android project with details as listed in the table below.

Property name
Property value
Project name
SRM_UploadFileRemoteService
Package name
in.ac.srmuniv.remoteservice
Activity name
-
Layout xml name
-
2.Create a file IRemoteFileUpload.aidl file and copy the code given below.
/* The package where the aidl file is located */
package in.ac.srmuniv.remoteservice;

/* The name of the remote service */
interface IRemoteFileUploadService {

   /* A simple Method which will return a message string */
     String uploadFile(String sourceFileUri);
}
Figure 1 Shows IRemoteFileUpload.aidl file added to source folder.Generated java file for the aidl file is also shown.
3.Create a Service SRM_FileUploadRemoteService.java and copy the code given below .
packagein.ac.srmuniv.remoteservice;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.StrictMode;
import android.util.Log;

public class SRM_FileUploadRemoteService extends Service {
   
private static final String SRMUNIV_INTENT_ACTION_BIND_MESSAGE_SERVICE = "srmuniv.intent.action.bindMessageService";
   
private final static String LOG_TAG = SRM_FileUploadRemoteService.class
           
.getCanonicalName();

   
@Override
   
public void onCreate() {
       
super.onCreate();
        Log.d(
LOG_TAG, "The SRM_FileUploadRemoteService was created.");
    }

   
@Override
   
public void onDestroy() {
        Log.d(
LOG_TAG, "The SRM_FileUploadRemoteService was destroyed.");
       
super.onDestroy();
    }

   
@Override
   
public IBinder onBind(Intent intent) {
       
if (SRMUNIV_INTENT_ACTION_BIND_MESSAGE_SERVICE.equals(intent
                .getAction())) {
            Log.d(
LOG_TAG, "The SRM_FileUploadRemoteService was binded.");
           
return new UploadFileService();
        }
       
return null;
    }

    String
response = null;

    String uploadForRemoteService(String sourceFileUri) {
        StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);
        String upLoadServerUri =
"http://navinsandroidtutorial.comlu.com/upload/upload.php";
        String fileName = sourceFileUri;
        Log.e(
"uploadFile", sourceFileUri);
        HttpURLConnection conn =
null;
        DataOutputStream dos =
null;
        String lineEnd =
"\r\n";
        String twoHyphens =
"--";
        String boundary =
"*****";
       
int bytesRead, bytesAvailable, bufferSize;
       
byte[] buffer;
       
int maxBufferSize = 1 * 1024 * 1024;
        File sourceFile =
new File(sourceFileUri);
       
if (!sourceFile.isFile()) {
            Log.e(
"uploadFile", "Source File Does not exist");
           
response = "Source File Does not exist";
           
return response;
        }
       
try { // open a URL connection to the PHP
           
FileInputStream fileInputStream = new FileInputStream(sourceFile);
            URL url =
new URL(upLoadServerUri);
            conn = (HttpURLConnection) url.openConnection();
// Open a HTTP
            // connection to the URL
           
conn.setDoInput(true); // Allow Inputs
           
conn.setDoOutput(true); // Allow Outputs
           
conn.setUseCaches(false); // Don't use a Cached Copy
           
conn.setRequestMethod("POST");
            conn.setRequestProperty(
"Connection", "Keep-Alive");
            conn.setRequestProperty(
"ENCTYPE", "multipart/form-data");
            conn.setRequestProperty(
"Content-Type",
                   
"multipart/form-data;boundary=" + boundary);
            conn.setRequestProperty(
"uploaded_file", fileName);
            dos =
new DataOutputStream(conn.getOutputStream());
            dos.writeBytes(twoHyphens + boundary + lineEnd);
            dos.writeBytes(
"Content-Disposition: form-data; name=\"uploaded_file\";filename=\""
                   
+ fileName + "\"" + lineEnd);
            dos.writeBytes(lineEnd);
            bytesAvailable = fileInputStream.available();
// create a buffer ofmaximum size
           
bufferSize = Math.min(bytesAvailable, maxBufferSize);
            buffer =
new byte[bufferSize];
           
// read file and write it into form...
           
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
           
while (bytesRead > 0) {
                dos.write(buffer,
0, bufferSize);
                bytesAvailable = fileInputStream.available();
                bufferSize = Math.min(bytesAvailable, maxBufferSize);
                bytesRead = fileInputStream.read(buffer,
0, bufferSize);
            }

           
// send multipart form data necesssary after file data...
           
dos.writeBytes(lineEnd);
            dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

           
// Responses from the server (code and message)
           
int serverResponseCode = conn.getResponseCode();
            String serverResponseMessage = conn.getResponseMessage();

            Log.i(
"uploadFile", "HTTP Response is : " + serverResponseMessage
                    +
": " + serverResponseCode);

           
if (serverResponseCode == 200)
               
response = "File uploaded Sucessfully";
           
else
               
response = "File not uploaded Sucessfully";

           
// close the streams //
           
fileInputStream.close();
            dos.flush();
            dos.close();

        }
catch (MalformedURLException ex) {
           
response = "Url not found";
            ex.printStackTrace();
            Log.e(
"Upload file to server", "error: " + ex.getMessage(), ex);

        }
catch (IOException e) {
            e.printStackTrace();
            
response = "error in upload";
            Log.e(
"Server Exception","Exception : " + e.getMessage(), e);
        }
       
return response;
    }

   
public class UploadFileService extends IRemoteFileUploadService.Stub {
       
@Override
       
public String uploadFile(String url) throws RemoteException {
           
return uploadForRemoteService(url);
        }
    }

}

4.Modify Androidmanifest.xml
<?xml version="1.0"encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="in.ac.srmuniv.remoteservice"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="3"/>

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
        <service android:name="SRM_FileUploadRemoteService">
            <intent-filter>
                <action android:name="srmuniv.intent.action.bindMessageService">
                </action>
            </intent-filter>
        </service>
    </application>
</manifest>

5.Run the application in the emulator. 
 Since no activity is involved there is no output is seen.but you have to run the service.

The source code for upload.php is given below copy it in the folder c:/xampp/htdocs/upload/upload.php
if you are using xampp server
<?php
$target_path1 = "uploads/";
/* Add the original filename to our target path.
Result is "uploads/filename.extension" */
$target_path1 = $target_path1 . basename( $_FILES['uploaded_file']['name']);
if(move_uploaded_file($_FILES['uploaded_file']['tmp_name'], $target_path1)) {
    echo "The first file "basename( $_FILES['uploaded_file']['name']).
    " has been uploaded.";
} else{
    echo "There was an error uploading the file, please try again!";
    echo "filename: "basename( $_FILES['uploaded_file']['name']);
    echo "target_path: " .$target_path1;
}

?>
CODE EXPLANATION
The AIDL file created in src folder would make the Eclipse+AIDL tool will generate a file with the same name, but .java extension .The file is generated in gen folder /gen/in.ac.srmuniv.remoteservice/ IRemoteFileUpload.java file. This is an auto-generated file one should not edit it. It contains a Stub class that will be implemented in  our remote service.
 public IBinder onBind(Intent intent) {
  if (SRMUNIV_INTENT_ACTION_BIND_MESSAGE_SERVICE.equals(intent
                     .getAction())) {
                Log.d(LOG_TAG, "The SRM_FileUploadRemoteService was binded.");
                return new UploadFileService();
           }
           return null;
     }
        To implement our remote service, we'll return the IBinder from onBind() method in our service class,SRM_FileUploadRemoteService. The IBinder represents the implementation of the remote service. To implement IBinder
     public class UploadFileService extendsIRemoteFileUploadService.Stub {
           @Override
           public String uploadFile(String url) throws RemoteException {
                returnuploadForRemoteService(url);
           }
     }
we subclass IRemoteFileUploadService.Stub class from the auto-generated Java code, and provide implementation for our AIDL-defined methods, in our case uploadFile(String url).
      Once we have the service implement the onBind() properly, we are ready to connect to that service from our client. 
HttpUrlconnection with necessary header properties set enables to post values to the server.In our app we have a hard coded url  "http://10.0.2.2/upload/upload.php".The server side code should written compatible to the  app to receive the posted  values from app for processing.


1.Create  Android project with details as listed in the table below.
Property name
Property value
Project name
SRM_FileUploadClient
Package name
in.ac.srmuniv.remoteclient
Activity name
SRM_FileUploadClient
Layout xml name
activity_main

2.Copy with package the IRemoteFileUpload.aidl file to the source folder of the client application


Figure 2 Shows copied IRemoteFileUpload.aidl file to the source folder.Generated java stub file is also shown.

3.Copy the code  to the file activity_main.xml in res/layout folder 
<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="20dp"
    android:orientation="vertical" >

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TableRow
            android:id="@+id/tableRow1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >

            <TextView
                android:id="@+id/textView1"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="ChooseFile"
                android:textAppearance="?android:attr/textAppearanceLarge"/>

            <EditText
                android:id="@+id/editText1"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:ems="10" >

                <requestFocus />
            </EditText>
        </TableRow>

        <TableRow
            android:id="@+id/tableRow2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >

            <TextView
                android:id="@+id/textView2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Note: Enter file name with if the file is in folder enter relative path eg: srm/resume.txt" />
        </TableRow>

        <TableRow
            android:id="@+id/tableRow3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >

            <Button
                android:id="@+id/uploadButton"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Bind" />

            <Button
                android:id="@+id/disconnectButton"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Disconnct" />
        </TableRow>

        <TableRow
            android:id="@+id/tableRow4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >

            <TextView
                android:id="@+id/messageTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text=""
                android:textAppearance="?android:attr/textAppearanceLarge"/>
        </TableRow>
    </TableLayout>


</LinearLayout>
4.Copy the code in SRM_FileUploadClient .java. Activity.
packagein.ac.srmuniv.remoteclient;

importin.ac.srmuniv.remoteclient.R;
importin.ac.srmuniv.remoteservice.IRemoteFileUploadService;

importandroid.app.Activity;
importandroid.content.ComponentName;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.content.ServiceConnection;
import android.os.Bundle;
importandroid.os.Environment;
import android.os.Handler;
import android.os.IBinder;
importandroid.os.RemoteException;
import android.util.Log;
importandroid.view.Gravity;
import android.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.Button;
importandroid.widget.EditText;
importandroid.widget.Toast;

public class SRM_FileUploadClientActivity extends Activity {
     protected static final String LOG_TAG = "DisplayClient";
     private Button disconnectButton;
     private Button uploadButton;
     private EditText edittext;

     privateIRemoteFileUploadService IRservice;

     @Override
     public void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
           disconnectButton = (Button) findViewById(R.id.disconnectButton);
           uploadButton = (Button) findViewById(R.id.uploadButton);
           edittext = (EditText) findViewById(R.id.editText1);
           disconnectButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                     disconnectService();
                }
           });

           uploadButton.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                     Runnable r = new Runnable() {

                           @Override
                           public void run() {
                                uploadFilesServiceCall();

                           }
                     };
                     Handler h = new Handler();
                     h.post(r);
                }
           });
     }

     private ServiceConnection mConnection = new ServiceConnection() {

           public voidonServiceConnected(ComponentName className, IBinder service) {
                IRservice = IRemoteFileUploadService.Stub.asInterface(service);

           }

           public voidonServiceDisconnected(ComponentName className) {
                IRservice = null;

           }
     };

     public void connectService() {
           if (IRservice == null) {
                Intent bindIntent = new Intent(
                           "srmuniv.intent.action.bindMessageService");
                bindIntent.setClassName("in.ac.srmuniv.remoteservice",
                           "in.ac.srmuniv.remoteservice"
                                     + ".SRM_FileUploadRemoteService");
                bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);
                Log.d(LOG_TAG,
                           "The Service will be connected soon (asynchronus call)!");
           }
     }

     public void disconnectService() {
           if (IRservice != null) {
                IRservice = null;
                unbindService(mConnection);
                Log.d(LOG_TAG, "The connection to the service was closed.!");
                uploadButton.setText("Bind");
           }

     }

     public voiduploadFilesServiceCall() {
           Log.d(LOG_TAG, "Uploading file from the Service.");
           if (IRservice == null) { // if the service is null the //connection is not established.
                Log.d(LOG_TAG, "The service was not connected -> connecting.");
                connectService();
                uploadButton.setText("Upload");

           } else {
                Log.d(LOG_TAG, "The Service is already connected Uploading file.");
                try {
                     String message = IRservice.uploadFile(Environment
                                .getExternalStorageDirectory().getPath()
                                + "/"
                                + edittext.getText().toString());
                     Toast toast = Toast.makeText(getApplicationContext(), message,
                                Toast.LENGTH_LONG);
                     toast.setGravity(Gravity.TOP, 25, 400);
                     toast.show();
                } catch (RemoteException e) {
                     Log.e(LOG_TAG, "An error occured during the service call.");
                }
           }
     }
}

5.No need to ModifyAndroidmanifest.xml
<?xml version="1.0"encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="in.ac.srmuniv.remoteclient"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="3"/>

    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
<activity android:name="in.ac.srmuniv.remoteclient.SRM_FileUploadClientActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
           <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
 </manifest>
6. Push a File to SD Card

Figure 3 Shows a text file is pushed to SD card in emulator 
6.Run the application in the emulator. 

 Figure 4 Shows clicking to bind the service.               Figure 5 Shows file from SD card typed in                                                                                                      edit text uploaded to server successfully                                                                                                                                                                         
 Figure 6 Shows uploaded file from android device is available in the server.Type the URL in web browser to view the file content of the uploaded file.
CODE EXPLANATION
              Copying the IRemoteFileUpload.aidl file in appropriate package in SRM_FileUploadClient application from SRM_FileUploadRemoteService application generates IRemoteFileupload.java stub file in gen folder.
The following code in SRM_FileUploadClientActivity is for Service Connection object.
     private ServiceConnection mConnection = new ServiceConnection() {
           public void onServiceConnected(ComponentName className, IBinder service) {
                IRservice = IRemoteFileUploadService.Stub.asInterface(service);
           }
           public void onServiceDisconnected(ComponentName className) {
                IRservice = null;
           }
     };
           The client application SRM_FileUploadClient  consumes the remote service .We have SRM_FileUploadClientActivity connects to the service. The anonymous ServiceConnection type class implement onServiceConnected() and onServiceDiconnected() methods. These callback methods  will get the stub implementation of the remote service upon connection. We need to cast them from stubs to our AIDL service implementation. To do that, we use the following code  IRemoteFileUpload.Stub.asInterface((IBinder) boundService) helper method.
The following code in SRM_FileUploadClientActivity is for binding service when button is clicked.
bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);
                Log.d(LOG_TAG,
                           "The Service will be connected soon (asynchronous call)!");
   When Bind button is clicked bindService() is called which will create the service if it is not created. referenced by bindIntent object it also takes ServiceConnection object as argument which enables communication with service through the IRservice object.
The following code will be called when upload button is clicked.
String message = IRservice.uploadFile(Environment
                                .getExternalStorageDirectory().getPath()
                                + "/"
                                + edittext.getText().toString());
When upLoad button is clicked through IRservice object (IRemoteFileUpload remote service object) upLoadFile() method is called.The file name of the file in SD card is typed  through EditText  and passed as the agument for the remote method call.It is preferable to use third party FileChooser app for selecting files to upload from device.

    String uploadForRemoteService(String sourceFileUri) {
        StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();

the above code is needed other wise networkOnmainThread error will occur.But it is recommended to use AysncTask instead.