Using BZip2 in Xamarin - android

I am trying to read from a file, and I want to use Bzip2InputStream to do it. When i try to run my application it runs fine untill the code reaches the creation of the inputstream. At wich point it keeps running, but nothing happens and roughly every second the application output reads:
[Mono] GC_MINOR: (Nursery full) pause 5.67ms, total 6.16ms, bridge 0.00ms promoted 192K major 1152K los 1075K
Is there an alternatives I do not know about? I am doing something stupid? Thanks for any help!
The code is as follows.
using Ionic.BZip2;
namespace ActionBarTest
{
public class CoverageElement
{
}
public class Coverages
{
private List<string> ChartNames = new List<string> ();
private List<CoverageElement> m_Coverages;
private BZip2InputStream bz2Stream;
public Coverages(Stream compressedCoverage)
{
try{
bz2Stream = new Ionic.BZip2.BZip2InputStream(compressedCoverage);
}
catch (Exception error){
}
using (BinaryReader b = new BinaryReader (bz2Stream)) {
int t1 = b.ReadInt32 ();
Console.WriteLine ("krøll i binaryreader" + t1);
int noOfElements = b.ReadInt32 ();
for (int i = 0; i < noOfElements; i++) {
char[] t = b.ReadChars (8);
ChartNames.Add (new string (t));
}
}
}
}
}

I found out the answer, instead of sending the stream directly from the webclient result I needed to make a memorystream from the webclient result, and use that.

You need to make a memorystream from the webclient result and use that. You can't just send the stream directly from the webclient result. Memorystreams always help, and it makes everything easier to read, and sometimes it's the key to doability altogether.

Related

UnityWebRequest does nothing on IL2CPP Build

I've been trying to simply call an api on an android build supporting 64 bit (IL2CPP build) and the UnityWebRequest class didnt seem to work. It's being called via a simple ui button click. It hits the webRequest.SendWebRequest(); and nothing happens. Ive tried the following samples. One, directly from the Unity docs for UnityWebRequest and others using standard HttpClient.
UnityWebRequest:
IEnumerator GetRequest(string uri)
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
webRequest.SetRequestHeader("Authorization", "Bearer " + API_KEY);
yield return webRequest.SendWebRequest();
if (webRequest.isNetworkError)
{
debugText.text = ": Error: " + webRequest.error;
coroutineAllowed = false;
}
else
{
debugText.text = ":\nReceived: " + webRequest.downloadHandler.text;
dynamic jsonObj = JsonConvert.DeserializeObject(webRequest.downloadHandler.text);
foreach (var obj in jsonObj["businesses"])
{
businessResults.Add(new Business()
{
name = (string)obj["name"],
image_url = (string)obj["image_url"],
review_count = (string)obj["review_count"],
rating = (string)obj["rating"],
Coordinates = new Coordinates()
{
Latitude = (float)obj["coordinates"]["latitude"],
Longitude = (float)obj["coordinates"]["longitude"]
},
price = (string)obj["price"]
});
}
debugText.text = businessResults.Count.ToString();
//coroutineAllowed = true;
}
debugText.text = "getRequest 4";
}
}
This unfortunately did nothing at the yield return webRequest.SendWebRequest();
The next sample I tried was using HttpClient():
IEnumerator HttpClientCall(string uri) //possibly wrap in IEnumerator
{
debugText.text += "http coroutine started" +Environment.NewLine;
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", API_KEY);
var response = httpClient.GetAsync(uri);
if (response.Result.StatusCode != HttpStatusCode.OK)
{
debugText.text += "FAILED HTTP GET";
}
yield return response.Result.Content.ReadAsStringAsync();
dynamic jsonObj = JsonConvert.DeserializeObject(response.Result.Content.ReadAsStringAsync().Result);
foreach (var obj in jsonObj["businesses"])
{
businessResults.Add(new Business()
{
name = (string)obj["name"],
image_url = (string)obj["image_url"],
review_count = (string)obj["review_count"],
rating = (string)obj["rating"],
Coordinates = new Coordinates()
{
Latitude = (float)obj["coordinates"]["latitude"],
Longitude = (float)obj["coordinates"]["longitude"]
},
price = (string)obj["price"]
});
debugText.text += Environment.NewLine + ((string)obj["name"]);
}
}
}
Once again, nothing when it hits yield return response.Result.Content.ReadAsStringAsync();
These all work on PC, and they both return results that i'm expecting.
The next thing i heard was about setting the android manifest application tag with android:usesCleartextTraffic="true"
This unfortunately, also did nothing for me lol. I know it has to be the 64 support, because this works on a standard build. The moment i go to build with 64 support, it doesnt work.
Any help on why it's not returning appropriately would be very helpful.
side note, i know the code is pretty ugly, but after i can figure out why the build doesnt work on the device a heavy refactoring is going to be in play. Thanks in advance!
So after a lot of trouble shooting ive found out why this was not working. The main issue seems to be stemming from my use of the standard Newtonsoft Json package when Unity, apparently, has their own internal JsonUtility class. After changing this:
dynamic jsonObj = JsonConvert.DeserializeObject(response.Result.Content.ReadAsStringAsync().Result);
To This:
var js = JsonUtility.FromJson<T>(response.Result.Content.ReadAsStringAsync().Result);
my results are finally showing in the the apk build correctly.
Also, to note that to map correctly, the JsonUtility.FromJson must be typed to a class that exactly mirrors the incoming json object explicitly.
The page article that finally helped me with this issue is here.
P.S.
Thank you to #RetiredNinja for trying to help instead of just downvoting and saying nothing of value. You're amazing!

Get personal app code and display it

I am trying to get the app code and display it, for an example if button X starts a new activity then a textView displays the whole method
I reached only how can I display code in HTML format from this question
But is there is a way to get the code of my app out, I think that there are 2 ways
An Internal one by getting it by the app itself
An External one by reading the java file then filtering it and getting the text of the method
Is there are any ideas about that?
Thanks in advance
The above is not currently possible as mentioned by others is the comments. What i can suggest is shipping your application with the source code in the assets folder and using a helper function to extract a certain methods from the source at runtime (your second proposed approach). I have written example code but it is in pure java and needs to be ported to android (a few lines).
NB: You may need to reformat the code after extraction depending on your use case.
Hope it helps :)
The code for the helper method:
static String getTheCode(String classname ,String methodSignature ) throws FileNotFoundException {
//**********************A few lines of code below need changing when porting ***********//
// open file, your will be in the assets folder not in the home dir of user, don't forget the .java extension when porting
File file = new File(System.getProperty("user.home") +"/"+ classname +".java");
// get the source, you can use FileInputReader or some reader supported by android
Scanner scanner = new Scanner(file);
String source = "";
while(scanner.hasNext()) {
source += " "+ scanner.next();
}
//**********************The above code needs changing when porting **********//
// extract code using the method signature
methodSignature = methodSignature.trim();
source = source.trim();
//appending { to differentiate from argument as it can be matched also if in the same file
methodSignature = methodSignature+"{";
//making sure we find what we are looking for
methodSignature = methodSignature.replaceAll("\\s*[(]\\s*", "(");
methodSignature = methodSignature.replaceAll("\\s*[)]\\s*", ")");
methodSignature = methodSignature.replaceAll("\\s*[,]\\s*", ",");
methodSignature = methodSignature.replaceAll("\\s+", " ");
source =source.replaceAll("\\s*[(]\\s*", "(");
source = source.replaceAll("\\s*[)]\\s*", ")");
source = source.replaceAll("\\s*[,]\\s*", ",");
source = source.replaceAll("\\s+", " ");
if(!source.contains(methodSignature)) return null;
// trimming all text b4 method signature
source = source.substring(source.indexOf(methodSignature));
//getting last index, a methods ends when there are matching pairs of these {}
int lastIndex = 0;
int rightBraceCount = 0;
int leftBraceCount = 0;
char [] remainingSource = source.toCharArray();
for (int i = 0; i < remainingSource.length ; i++
) {
if(remainingSource[i] == '}'){
rightBraceCount++;
if(rightBraceCount == leftBraceCount){
lastIndex = (i + 1);
break;
}
}else if(remainingSource[i] == '{'){
leftBraceCount++;
}
}
return source.substring(0 ,lastIndex);
}
Example usage (getTheCode methods is static and in a class called GetTheCode):
public static void main(String... s) throws FileNotFoundException {
System.out.println(GetTheCode.getTheCode("Main", "private static void shoutOut()"));
System.out.println(GetTheCode.getTheCode("Main", "private static void shoutOut(String word)"));
}
Output:
private static void shoutOut(){ // nothing to here }
private static void shoutOut(String word){ // nothing to here }
NB: When starting your new activity create a method eg
private void myStartActivty(){
Intent intent = new Intent(MyActivity.this, AnotherActivity.class);
startActivity(intent);
}
Then in your onClick:
#Override
public void onClick(View v) {
myStartActivity();
myTextView.setText(GetTheCode.getTheCode("MyActivity","private void myStartActivity()"));
}
Update: Ported the Code for android:
import android.content.Context;
import java.io.IOException;
import java.util.Scanner;
public class GetTheCode {
static String getTheCode(Context context, String classname , String methodSignature ) {
Scanner scanner = null;
String source = "";
try {
scanner = new Scanner(context.getAssets().open(classname+".java"));
while(scanner.hasNext()) {
source += " "+ scanner.next();
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
scanner.close();
// extract code using the method signature
methodSignature = methodSignature.trim();
source = source.trim();
//appending { to differentiate from argument as it can be matched also if in the same file
methodSignature = methodSignature+"{";
//making sure we find what we are looking for
methodSignature = methodSignature.replaceAll("\\s*[(]\\s*", "(");
methodSignature = methodSignature.replaceAll("\\s*[)]\\s*", ")");
methodSignature = methodSignature.replaceAll("\\s*[,]\\s*", ",");
methodSignature = methodSignature.replaceAll("\\s+", " ");
source =source.replaceAll("\\s*[(]\\s*", "(");
source = source.replaceAll("\\s*[)]\\s*", ")");
source = source.replaceAll("\\s*[,]\\s*", ",");
source = source.replaceAll("\\s+", " ");
if(!source.contains(methodSignature)) return null;
// trimming all text b4 method signature
source = source.substring(source.indexOf(methodSignature));
//getting last index, a methods ends when there are matching pairs of these {}
int lastIndex = 0;
int rightBraceCount = 0;
int leftBraceCount = 0;
char [] remainingSource = source.toCharArray();
for (int i = 0; i < remainingSource.length ; i++
) {
if(remainingSource[i] == '}'){
rightBraceCount++;
if(rightBraceCount == leftBraceCount){
lastIndex = (i + 1);
break;
}
}else if(remainingSource[i] == '{'){
leftBraceCount++;
}
}
return source.substring(0,lastIndex);
}
}
Usage:
// the method now takes in context as the first parameter, the line below was in an Activity
Log.d("tag",GetTheCode.getTheCode(this,"MapsActivity","protected void onCreate(Bundle savedInstanceState)"));
Let's start with a broader overview of the problem:
Display App code
Press X button
Open new activity with a textview which displays the method
The goal is to do the following:
Viewing app method by extracting it and then building & running it.
There are some methods we can use to run Java/Android code dynamically. The way I would personally do it is DexClassLoader and with Reflection.
If you need more details, let me know. Here is what it'd do though:
View app method
Upon pressing X, launch intent with extra to new Activity
Parse and compile code dynamically and then run it with DexClassLoader and Reflection
Sources:
Sample file loading Java method from TerminalIDE Android App
Android Library I made for Auto-Updating Android Applications without needing the Play Store on non-root devices

Is it a good way to call AsyncTask in infinity loop to pull data from server?

Well in app I'm trying to pull the data from sever for every 4 sec,and update the app.
I'm using handler,in that I'm calling AsynTask to fetch the data from server for every 4 sec.
Just I'm worried about the instance created for AsynTask every 4'sec causes any problem ?
This is what I'm doing.
private static final int DELAY = 1000 * 4;
final Handler printHandler = new Handler();
private boolean keepLooping = true;
printHandler.postDelayed(printStuff, DELAY);
Runnable printStuff = new Runnable(){
#Override
public void run(){
// call AsynTask to perform network operation on separate thread
new DownloadMainScore().execute("http://server/root/score.php");
if(keepLooping)
printHandler.postDelayed(this, DELAY);
}
};
On your choice of concurrency tool:
You are right that this is not so good. AsyncTasks are designed to be useful helpers when designing occasional asynchronous calls that then need to update a UI. As such, in old (< 1.6) versions of Android the maximum thread pool size was 10!
It would be better to go straight to the very robust Java out of which AsyncTask is built. Given you want to do this repeatedly, try a ScheduledExecutorService. I see they've even made a nice example for you.
Or, given that you seem to be getting a score down, best might be to maintain a persistent connection over a protocol like XMPP, for which there are many Java server and clients.
Finally, you might like to look at gcm.
On design issues in general
I see you want to print a score frequently. Once every four seconds in fact. But what's the point is the score hasn't changed? Furthermore, what if you've got a slow internet connection, and eight seconds later the one for four seconds ago hasn't finished? Right now you will set off yet another download request, even though the other one when it comes back will be up to date!
The solution is to decouple the download mechanism and the UI update mechanism. One way to do it is to have your scheduled download on a single threaded executor- not something you can control in an AsyncTask, which when finishes causes the UI to update and show the score.
Wishing you the best of luck!
Code sketch
Don't have environment set up right now, but in a very rough code sketch (check syntax), using a scheduled executor would look like:
In class:
private final ScheduledExecutorService downloadScheduler = Executors.newSingleThreadScheduledExecutor(1);
Then elsewhere, wherever you start doing this
final Runnable scoreHttpRunnable = new Runnable() {
#Override public void run() {
...
//do Http Syncronously here- I guess whatever is in the doInBackground(...) part of that Async task you wrote!
...
final int newScoreResult = ... (do whatever you do here)
...
runOnUiThread(new Runnable() { #Override public void run() { yourView.updateHoweverYouLike(newScoreResult); } })
...
};
downloadScheduler.scheduleAtFixedRate(scoreHttpRunnable, 0, 4, TimeUnit.SECONDS);
Going one of the other two routes is really too much to post in a single answer to a question. That'd be a another SO question if there isn't already one.
Be sure that next call send to asyc class only after once its done for that make a variable(IsLoadRunning) and make it true in on preExecute() and false in onPOstExecute and add a condition if(!IsLoadRunning){new DownloadMainScore().execute();}
As official documentation states
AsyncTasks should ideally be used for short operations (a few seconds at the most.)
Services can serve better in you case. Have a look at the accepted answer here
#Override
protected String doInBackground(String... params) {
Log.d(TAG, "type - " + params[0] + ", url = " + params[1] + ", name = " + params[2]);
downloadFile(params[1], params[2]);
return null;
}
here is download method
URL url = new URI(Url.replace(" ", "%20")).toURL();
URLConnection connection = url.openConnection();
connection.setConnectTimeout(1000);
int fileLength = connection.getContentLength();
mSavePath = CommonUtilities.getFileSavePath(mContext, fileName, fileLength);
Log.d(TAG, "*** saveFilePath - " + mSavePath);
InputStream inputStream = connection.getInputStream();
if (inputStream != null) {
File file = new File(mSavePath);
BufferedOutputStream bufferOutputStream = new BufferedOutputStream(new FileOutputStream(file));
byte byteArray[] = new byte[1024];
int len = 0;
long total = 0;
while ((len = inputStream.read(byteArray)) != -1) {
bufferOutputStream.write(byteArray, 0, len);
total += len;
}
bufferOutputStream.flush();
bufferOutputStream.close();
inputStream.close();
} else {
Log.d(TAG, "*** inputStream is null");
}

Create a fast file search function in android

I'm new to android and I'm trying to develop file explorer which includes search function. I'm using a recursive search function that works fine in folders with a few subfolders and files, but for some reason it's EXTREMELY SLOW and could "Force Close" in folders with lots of subfolders and files, because there's not enough memory. I do the search by creating ArrayList where the results will be placed, and then calling the recursive function that will fill the list. The "path" argument is the file where the search will start from, and "query" is the search query.
ArrayList<File> result = new ArrayList<File>();
fileSearch(path, query, result);
this is what the recursive function looks like:
private void fileSearch(File dir, String query, ArrayList<File> res) {
if (dir.getName().toLowerCase().contains(query.toLowerCase()))
res.add(dir);
if (dir.isDirectory() && !dir.isHidden()) {
if (dir.list() != null) {
for (File item : dir.listFiles()) {
fileSearch(item, query, res);
}
}
}
}
If someone could point me to a way of performing a faster and/or more efficient file search, I would really appreciate that.
EDIT:
This is how I tried to do the job with AsyncTask:
private class Search extends AsyncTask<File, Integer, Void> {
String query;
ArrayList<File> result = new ArrayList<File>();
public Search(String query){
this.query = query;
setTitle("Searching");
}
#Override
protected Void doInBackground(File... item) {
int count = item.length;
for (int i = 0; i < count; i++) {
fileSearch(item[i], query, result);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return null;
}
protected void onProgressUpdate(Integer... progress) {
setProgress(progress[0]);
}
protected void onPostExecute() {
searchResults = new ListItemDetails[result.size()];
for (int i = 0; i < result.size(); i++) {
File temp = result.get(i);
if (temp.isDirectory())
searchResults[i] = new ListItemDetails(temp.getAbsolutePath(),
R.drawable.folder, temp.lastModified(), temp.length());
else {
String ext;
if (temp.getName().lastIndexOf('.') == -1)
ext = "";
else
ext = temp.getName().substring(
temp.getName().lastIndexOf('.'));
searchResults[i] = new ListItemDetails(temp.getAbsolutePath(),
getIcon(ext), temp.lastModified(), temp.length());
}
}
finishSearch();
}
}
public void finishSearch() {
Intent intent = new Intent(this, SearchResults.class);
startActivity(intent);
}
The call to finishSearch() is just so I can create the Intent to show the results in other Activity. Any ideas, suggestions, tips? Thanks in advance
It is possible that you are hitting symbolic links and going into an infinitive loop with your search function and depleting available memory to your application.
I would suggest you to keep a separate list containing canonical paths (File.getCanonicalPath()) of directories you've visited and avoid visiting them over and over again.
Why don't you use Apache Commons IO? It has some functions to deal with searching.
I also suggest using the method FileUtils.listFiles, which takes a folder, a search query and a directory filter as parameters.
The following example returns you a list of all file's paths that matched according to a regex. Try adding it in doInBackground of your AsyncTask:
Collection files = FileUtils.listFiles(new File(yourRootPath),
new RegexFileFilter(searchQuery),
DirectoryFileFilter.DIRECTORY);
Have you looked into Lucene?
It is especially designed to index and query large numbers of free-text documents, so many of the I/O streaming and indexing tasks have already been solved for you. If you remove the recursion and do the document indexing using a Lucene index in a purely iterative fashion, memory issues may be mitigated.
Look into this thread:
Lucene in Android
Do it in the background, and starting from Android O (API 26) , you can use Files.find API. Exmaple:
Files.find(
Paths.get(startPath), Integer.MAX_VALUE,
{ path, _ -> path.fileName.toString() == file.name }
).forEach { foundPath ->
Log.d("AppLog", "found file on:${foundPath.toFile().absolutePath}")
}

Android app uses static method to transfer and plot data

I've been asking questions regarding my Android project that continually plots Bluetooth data in real-time.
Basically what I've already done is create a first version of my app by cobbling together some open source code Blueterm and OrientationSensorExample
It's been suggested that I add a thread, a handler, a Service, or use Async Task, or AIDL, etc. But I don't know how to use any of these and would appreciate an explanation.
Here's a description of the Blueterm open source code I started with (see link above). Blueterm is basically a terminal emulator program that communicates over Bluetooth. It consists of several activities with Blueterm being the most important. It discovers, pairs, and connects with a remote Bluetooth device that supports SPP/RfComm. When connected I can use Blueterm to configure the remote device by sending it commands to turn on sampling, change the number of channels to sample (to one channel), change to format of the incoming data (I like comma separated data), etc
Here's a description of the OrientationSensorExample open source code I started with (see link above). It's basically an example application of the AnroidPlot library. The OrientationSensor activity implements SensorEventListener. This includes overriding onSenorChanged() which is called whenever new orientation sensor data is taken, and it redraws the graph.
Having cobbled together these two open source projects (Blueterm and OrientationSensorExample) into one application (Blueterm) here's a description of how the overall application (Blueterm) works. When I start Blueterm the whole screen emulates a nice blue terminal. From the Options Menu I discover, pair with, connect to, and configure a remote bluetooth device as described above. Once I have configured the remote device, I go again to the Options Menu and select "Plot data" which launches the Plot activity. The terminal emulator goes away, and a nice scrolling real-time plot from the Plot activity shows up.
As far as I can tell there is a background thread that calls an update() method as follows:
/**
* Look for new input from the ptty, send it to the terminal emulator.
*/
private void update() {
int bytesAvailable = mByteQueue.getBytesAvailable();
int bytesToRead = Math.min(bytesAvailable, mReceiveBuffer.length);
try {
int bytesRead = mByteQueue.read(mReceiveBuffer, 0, bytesToRead);
append(mReceiveBuffer, 0, bytesRead);
//VTR use existing handler that calls update() to get data into plotting activity
Plot.plotData(mReceiveBuffer, 0, bytesRead);
} catch (InterruptedException e) {
//VTR OMG their swallowing this exception
}
}
In the update() method I found it convenient to call my Plot.plotData() method and pass it the same date that is passed to the append() method to plot the data. NOTE: This only works if plotData() is a static method. No one has been able to explain why.
Anyway plotData() is a static method and here's how it and it's helper methods look now:
private static StringBuffer strData = new StringBuffer("");
public static void plotData(byte[] buffer, int base, int length) {
Log.i("Entering: ", "plotData()");
/*
byte[] buffer = (byte[]) msg.obj;
int base = msg.arg1;
int length = msg.arg2;
*/
for (int i = 0; i < length; i++) {
byte b = buffer[base + i];
try {
if (true) {
char printableB = (char) b;
if (b < 32 || b > 126) {
printableB = ' ';
}
Log.w("Log_plotData", "'" + Character.toString(printableB)
+ "' (" + Integer.toString(b) + ")");
strData.append(Character.toString(printableB));
if (b == 10)
{
Log.i("End of line: ", "processBlueData()");
Log.i("strData", strData.toString());
splitData(strData);
strData = new StringBuffer("");
}
}
} catch (Exception e) {
Log.e("Log_plotData_exception", "Exception while processing character "
+ Integer.toString(i) + " code "
+ Integer.toString(b), e);
}
}
Log.i("Leaving: ", "plotData()");
}
private static void splitData(StringBuffer strBuf) {
String strDash = strBuf.toString().trim();
String[] strDashSplit = strDash.split("-");
for (int ndx = 0; ndx < strDashSplit.length; ndx++)
{
if (strDashSplit[ndx].length() > 0)
Log.i("strDashSplit", ndx + ":" + strDashSplit[ndx]);
String strComma = strDashSplit[ndx].trim();
String[] strCommaSplit = strComma.split(",");
for (int mdx = 0; mdx < strCommaSplit.length; mdx++)
{
if (strCommaSplit[mdx].length() > 0)
Log.i("strCommaSplit", mdx + ":" + strCommaSplit[mdx]);
if (mdx == 1)
{
int raw = Integer.parseInt(strCommaSplit[1],16);
Log.i("raw", Integer.toString(raw));
float rawFloat = raw;
Log.i("rawFloat", Float.toString(rawFloat));
float ratio = (float) (rawFloat/65535.0);
Log.i("ratio", Float.toString(ratio));
float voltage = (float) (5.0*ratio);
Log.i("voltage", Float.toString(voltage));
nowPlotData(voltage);
}
}
}
}
public static void nowPlotData(float data) {
// get rid the oldest sample in history:
if (plotHistory.size() > HISTORY_SIZE) {
plotHistory.removeFirst();
}
// add the latest history sample:
plotHistory.addLast(data);
// update the plot with the updated history Lists:
plotHistorySeries.setModel(plotHistory, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
//VTR null pointer exception?
if (plotHistoryPlot == null)
Log.i("aprHistoryPlot", "null pointer exception");
// redraw the Plots:
plotHistoryPlot.redraw();
}
If it is strongly recommended that plotData() not be a static method and that I should do something else please explain here and how. Thanks!
This might be a question much better suited for Code Review, rather than here. Perhaps you can reformulate to post it there, or trim it a lot to repost it here.
Furthermore, to answer: "It's been suggested that I add a thread, a handler, a Service, or use Async Task, or AIDL, etc. But I don't know how to use any of these and would appreciate an explanation.", the best advise would be to link you to a book about android, such as: http://commonsware.com/Android/ . Chapters 35 and 36 deal with services, while chapter 20 is about threads. You will never get an answer as complete as those chapters here.

Categories

Resources